跳到主要内容

🚀 Java 面向对象编程完全指南

理解类与对象,掌握Java面向对象编程的精髓! 面向对象编程(OOP)是Java的核心思想,掌握它就等于掌握了Java的灵魂!

📖 目录


🧠 面向对象编程基础

什么是类?什么是对象?

Java中最核心、最有趣的两个概念:对象。这就像是"造物主"的工作,理解了它们,你就拿到了进入Java世界大门的钥匙!🗝️

📋 什么是类(Class)?

,就是一张设计蓝图或者一个物种模板。🧾

🎯 核心概念

  • 抽象性:类是对现实世界事物的抽象
  • 模板性:类定义了对象应该有的属性和行为
  • 可复用性:一个类可以创建多个对象

🐱 生动比喻:猫咪设计图

比如,你想造一只猫,你得先有一份《猫的设计蓝图》:

  • 属性 (是什么):这只猫应该有颜色、品种、年龄...(这些叫成员变量
  • 行为 (能做什么):这只猫会喵喵叫、会卖萌、会跑...(这些叫成员方法

💻 代码示例:猫咪类设计

/**
* 猫咪类 - 这是一份设计蓝图,不是具体的猫
*/
public class Cat {

// 👉 属性/状态 (成员变量)
private String color; // 颜色
private String breed; // 品种
private int age; // 年龄
private boolean isHungry; // 是否饥饿

// 👉 构造方法 - 创建对象时使用
public Cat(String color, String breed, int age) {
this.color = color;
this.breed = breed;
this.age = age;
this.isHungry = true;
}

// 👉 行为/功能 (成员方法)
public void meow() {
System.out.println(color + "的" + breed + "在喵喵叫:喵~喵~");
}

public void eat(String food) {
if (isHungry) {
System.out.println(color + "的" + breed + "正在吃:" + food);
isHungry = false;
} else {
System.out.println(color + "的" + breed + "现在不饿");
}
}

public void sleep() {
System.out.println(color + "的" + breed + "正在睡觉...Zzz");
}

// 👉 Getter/Setter方法 - 控制属性访问
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }

public int getAge() { return age; }
public void setAge(int age) { this.age = age; }

// 👉 业务方法
public boolean isKitten() {
return age < 1;
}

@Override
public String toString() {
return String.format("猫{颜色='%s', 品种='%s', 年龄=%d岁, %s}",
color, breed, age, isHungry ? "饥饿" : "饱了");
}
}

🐈 什么是对象(Object)?

对象,就是根据蓝图真正制造出来的具体实物。🏠

这个过程叫做实例化,听起来很高大上,但其实就像用模具扣小饼干一样简单!🍪

🏗️ 实例化演示

public class CatExample {
public static void main(String[] args) {
// 根据'Cat'蓝图,造第一只具体的猫 -> 对象1号
Cat cat1 = new Cat("橙色", "橘猫", 2);
System.out.println("第一只猫:" + cat1);
cat1.meow(); // 输出:橙色的橘猫在喵喵叫:喵~喵~
cat1.eat("小鱼干"); // 输出:橙色的橘猫正在吃:小鱼干

// 根据同一份蓝图,再造第二只具体的猫 -> 对象2号
Cat cat2 = new Cat("灰色", "英短", 1);
System.out.println("\n第二只猫:" + cat2);
cat2.meow(); // 输出:灰色的英短在喵喵叫:喵~喵~
cat2.sleep(); // 输出:灰色的英短正在睡觉...Zzz

// 根据同一份蓝图,再造第三只猫 -> 对象3号
Cat cat3 = new Cat("白色", "布偶猫", 0); // 小猫
System.out.println("\n第三只猫:" + cat3);
System.out.println("是不是小猫? " + cat3.isKitten());

// 对象数组示例
Cat[] cats = {cat1, cat2, cat3};

System.out.println("\n=== 所有的猫 ===");
for (Cat cat : cats) {
System.out.println(cat.toString());
}
}
}

🎯 核心关系对比

概念比喻角色关键词代码示例
类 (Class)设计蓝图 📋模板、物种概念抽象模板public class Cat { }
对象 (Object)真正的房子 🏠具体实例、个体具体实体new Cat("橙色", "橘猫", 2)

💡 一句话记住:类是模板,对象是根据模板创造出的具体实例。先有类,再有对象。没有"猫"的概念(类),就不存在"我家的这只橘猫"(对象)。

🌟 OOP三大特性

面向对象编程有三个核心特性:

1. 封装 (Encapsulation) 🔒

概念:将数据和方法包装在类中,隐藏内部实现细节。

public class BankAccount {
private double balance; // 私有属性,外部无法直接访问

public BankAccount(double initialBalance) {
this.balance = initialBalance;
}

// 公开的安全访问方法
public void deposit(double amount) {
if (amount > 0) { // 添加验证逻辑
balance += amount;
System.out.println("存款成功,余额:" + balance);
}
}

public boolean withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
System.out.println("取款成功,余额:" + balance);
return true;
}
System.out.println("取款失败,余额不足或金额无效");
return false;
}

public double getBalance() {
return balance; // 只读访问
}
}

2. 继承 (Inheritance) 🧬

概念:子类继承父类的属性和方法,实现代码复用。

// 父类:动物
public class Animal {
protected String name;
protected int age;

public Animal(String name, int age) {
this.name = name;
this.age = age;
}

public void eat() {
System.out.println(name + "正在吃东西");
}

public void sleep() {
System.out.println(name + "正在睡觉");
}
}

// 子类:狗
public class Dog extends Animal {
private String breed;

public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造方法
this.breed = breed;
}

// 子类特有的方法
public void bark() {
System.out.println(breed + "犬" + name + "在汪汪叫");
}

// 重写父类方法
@Override
public void eat() {
System.out.println(breed + "犬" + name + "正在吃狗粮");
super.eat(); // 可以调用父类方法
}
}

3. 多态 (Polymorphism) 🎭

概念:同一个接口,使用不同的实例而执行不同操作。

public class PolymorphismDemo {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal[] animals = {
new Dog("小黑", 3, "拉布拉多"),
new Cat("小白", 2, "波斯猫"),
new Dog("小黄", 1, "柯基")
};

// 同样的调用,不同的行为
for (Animal animal : animals) {
System.out.println("\n喂养:" + animal.name);
animal.eat(); // 多态调用

// 检查具体类型,调用特有方法
if (animal instanceof Dog) {
((Dog) animal).bark(); // 向下转型
} else if (animal instanceof Cat) {
((Cat) animal).meow();
}
}
}
}

💡 为什么选择面向对象编程?

这种"类-对象"的思维模式,是Java的核心哲学——面向对象编程(OOP)。

✅ 优势分析

  1. 🧩 模块化:把代码像乐高一样拆分和组装,结构清晰
  2. ♻️ 复用性:一份蓝图(类)可以造出无数个对象,避免重复代码
  3. 🛠️ 易维护:想修改所有猫的行为?直接改蓝图(类)就行了
  4. 🔒 安全性:通过封装保护数据,防止意外修改
  5. 🎭 灵活性:通过多态实现不同对象的统一处理
  6. 📈 扩展性:通过继承轻松扩展功能,不影响现有代码

🎯 实际应用场景

// 用户管理系统示例
public class UserManagementSystem {
public static void main(String[] args) {
// 创建不同类型的用户
User[] users = {
new AdminUser("admin", "admin123", "超级管理员"),
new RegularUser("user1", "password1", "张三"),
new VIPUser("vip1", "vip123", "李四", 2023)
};

// 统一处理不同类型的用户
processUsers(users);
}

// 多态方法:可以处理任何用户类型
public static void processUsers(User[] users) {
for (User user : users) {
System.out.println("\n处理用户:" + user.getUsername());

user.login(); // 多态调用
user.showProfile(); // 多态调用

if (user instanceof AdminUser) {
((AdminUser) user).manageSystem();
}

user.logout(); // 多态调用
}
}
}

🚀 重要提醒:当你下次看到 new 这个关键字时,就在心里大喊:"变!给我根据这个类,实例化一个对象出来!" 这就是面向对象编程的魔力!


🏗️ 类的深入解析

类的组成部分

一个完整的类通常包含以下几个部分:

📋 类的基本结构

/**
* 用户类示例 - 展示类的完整结构
*/
public class User {

// 1. 成员变量(属性)
private String username; // 用户名
private String password; // 密码
private int age; // 年龄
private String email; // 邮箱
private boolean isActive; // 是否激活

// 2. 静态变量(类变量)
private static int userCount = 0; // 用户总数
public static final String DEFAULT_ROLE = "USER"; // 默认角色

// 3. 静态代码块
static {
System.out.println("User类被加载...");
// 初始化静态资源
}

// 4. 实例代码块
{
userCount++; // 每创建一个对象,用户总数加1
System.out.println("创建User对象,当前用户总数:" + userCount);
}

// 5. 构造方法
public User() {
this("unknown", "password", 0); // 调用其他构造方法
}

public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
this.isActive = true;
}

// 6. 成员方法(实例方法)
public void login() {
if (isActive) {
System.out.println(username + " 登录成功");
} else {
System.out.println("账号已被禁用");
}
}

public void logout() {
System.out.println(username + " 已退出登录");
}

// 7. 静态方法(类方法)
public static int getUserCount() {
return userCount;
}

public static boolean validateUsername(String username) {
return username != null && username.length() >= 3;
}

// 8. Getter和Setter方法
public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public boolean isActive() {
return isActive;
}

public void setActive(boolean active) {
isActive = active;
}

// 9. toString方法
@Override
public String toString() {
return String.format("User{username='%s', age=%d, isActive=%s}",
username, age, isActive);
}

// 10. equals和hashCode方法
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return username.equals(user.username);
}

@Override
public int hashCode() {
return username.hashCode();
}
}

构造方法

构造方法是用于创建对象的特殊方法,具有以下特点:

🏗️ 构造方法特性

  • 方法名与类名相同
  • 没有返回值类型
  • 在创建对象时自动调用
  • 可以重载
  • 可以使用this()调用其他构造方法
public class Student {
private String name;
private int age;
private String major;
private String studentId;

// 1. 无参构造方法
public Student() {
this("未知学生", 0, "未定专业");
System.out.println("调用无参构造方法");
}

// 2. 参数构造方法
public Student(String name, int age, String major) {
this(name, age, major, generateStudentId());
System.out.println("调用三参数构造方法");
}

// 3. 完整参数构造方法
public Student(String name, int age, String major, String studentId) {
this.name = name;
this.age = age;
this.major = major;
this.studentId = studentId;
System.out.println("调用完整参数构造方法");
}

// 4. 拷贝构造方法
public Student(Student other) {
this(other.name, other.age, other.major, other.studentId + "_copy");
System.out.println("调用拷贝构造方法");
}

// 5. 私有构造方法(用于单例模式)
private Student(String name) {
this.name = name;
System.out.println("调用私有构造方法");
}

// 工具方法
private static String generateStudentId() {
return "STU" + System.currentTimeMillis();
}

// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }

public String getStudentId() { return studentId; }

@Override
public String toString() {
return String.format("Student{name='%s', age=%d, major='%s', studentId='%s'}",
name, age, major, studentId);
}
}

🎯 构造方法使用示例

public class ConstructorDemo {
public static void main(String[] args) {
// 1. 调用无参构造方法
Student student1 = new Student();

// 2. 调用参数构造方法
Student student2 = new Student("张三", 20, "计算机科学");

// 3. 调用完整参数构造方法
Student student3 = new Student("李四", 21, "软件工程", "2023001");

// 4. 调用拷贝构造方法
Student student4 = new Student(student2);

System.out.println("\n所有学生信息:");
System.out.println("student1: " + student1);
System.out.println("student2: " + student2);
System.out.println("student3: " + student3);
System.out.println("student4: " + student4);
}
}

成员变量与方法

📊 成员变量的类型

public class VariableTypes {

// 1. 实例变量(对象级别的变量)
private String instanceVar = "实例变量";
private int instanceCount = 0;

// 2. 静态变量(类级别的变量)
private static String staticVar = "静态变量";
private static int staticCount = 0;

// 3. 常量(静态final变量)
public static final String CONSTANT = "常量";
public static final int MAX_SIZE = 100;

// 4. 枚举类型变量
private enum Status { ACTIVE, INACTIVE, PENDING }
private Status currentStatus = Status.ACTIVE;

// 5. 数组类型变量
private String[] tags = {"Java", "OOP", "Programming"};
private int[] scores = new int[10];

// 6. 对象引用变量
private Object objectRef;
private String[] stringArray;

// 实例方法
public void instanceMethod() {
instanceCount++;
System.out.println("实例方法访问实例变量:" + instanceVar);
System.out.println("实例方法访问静态变量:" + staticVar);
}

// 静态方法
public static void staticMethod() {
staticCount++;
// System.out.println(instanceVar); // 错误:静态方法不能访问实例变量
System.out.println("静态方法访问静态变量:" + staticVar);
System.out.println("静态方法访问常量:" + CONSTANT);
}

// 访问器方法
public String getInstanceVar() {
return instanceVar;
}

public static String getStaticVar() {
return staticVar;
}
}

🎯 方法类型详解

public class MethodTypes {

// 1. 实例方法(需要对象调用)
public void instanceMethod(String param) {
System.out.println("实例方法,参数:" + param);
}

// 2. 静态方法(类方法,可以直接通过类名调用)
public static void staticMethod(String param) {
System.out.println("静态方法,参数:" + param);
}

// 3. 返回值方法
public String returnTypeMethod() {
return "返回字符串";
}

public int calculateSum(int a, int b) {
return a + b;
}

// 4. void方法(无返回值)
public void voidMethod() {
System.out.println("无返回值的方法");
}

// 5. 可变参数方法
public void variableArgsMethod(String... args) {
System.out.println("可变参数数量:" + args.length);
for (String arg : args) {
System.out.println("参数:" + arg);
}
}

// 6. 递归方法
public int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}

// 7. 方法重载示例
public void overloadedMethod() {
System.out.println("无参数的重载方法");
}

public void overloadedMethod(String param) {
System.out.println("一个字符串参数的重载方法:" + param);
}

public void overloadedMethod(int param) {
System.out.println("一个整数参数的重载方法:" + param);
}

public void overloadedMethod(String param1, int param2) {
System.out.println("两个参数的重载方法:" + param1 + ", " + param2);
}
}

🏠 Object类详解

Object类是所有类的超类,Java中的每个类都直接或间接地继承自Object类。

核心方法介绍

方法描述重要程度
equals(Object obj)判断两个对象是否相等⭐⭐⭐⭐⭐
hashCode()返回对象的哈希码值⭐⭐⭐⭐⭐
toString()返回对象的字符串表示⭐⭐⭐⭐
clone()创建并返回对象的一个副本⭐⭐⭐
getClass()返回对象的运行时类⭐⭐⭐
wait()使当前线程等待⭐⭐
notify()唤醒在此对象监视器上等待的单个线程⭐⭐
notifyAll()唤醒在此对象监视器上等待的所有线程⭐⭐
finalize()垃圾回收器调用此方法

equals()与hashCode()

这两个方法在Java集合框架中至关重要,必须一起重写。

🎯 equals()方法规范

public class Person {
private String name;
private int age;
private String idCard;

public Person(String name, int age, String idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}

// 重写equals方法
@Override
public boolean equals(Object obj) {
// 1. 自反性:x.equals(x)必须返回true
if (this == obj) {
return true;
}

// 2. 非空性:x.equals(null)必须返回false
if (obj == null) {
return false;
}

// 3. 类型一致性:只有类型相同才能比较
if (getClass() != obj.getClass()) {
return false;
}

// 4. 属性比较:比较关键属性
Person person = (Person) obj;
return idCard.equals(person.idCard); // 使用唯一标识比较
}

// 重写hashCode方法
@Override
public int hashCode() {
// 使用相同属性生成哈希码
return idCard.hashCode();
}

// Getter方法
public String getName() { return name; }
public int getAge() { return age; }
public String getIdCard() { return idCard; }
}

🧪 equals()与hashCode()测试

public class EqualsHashCodeTest {
public static void main(String[] args) {
Person person1 = new Person("张三", 25, "110101199001011234");
Person person2 = new Person("张三", 25, "110101199001011234");
Person person3 = new Person("李四", 30, "110101199002022345");

System.out.println("person1.equals(person2): " + person1.equals(person2)); // true
System.out.println("person1.equals(person3): " + person1.equals(person3)); // false
System.out.println("person1.hashCode(): " + person1.hashCode());
System.out.println("person2.hashCode(): " + person2.hashCode());
System.out.println("person3.hashCode(): " + person3.hashCode());

// HashSet测试:相等的对象必须有相同的hashCode
Set<Person> personSet = new HashSet<>();
personSet.add(person1);
personSet.add(person2); // 不会被添加,因为equals返回true
personSet.add(person3);

System.out.println("Set中的元素数量: " + personSet.size()); // 2
}
}

📝 为什么重写equals()必须重写hashCode()?

/**
* 错误示例:只重写equals(),不重写hashCode()
*/
public class BadPerson {
private String name;

public BadPerson(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
BadPerson person = (BadPerson) obj;
return name.equals(person.name);
}

// 没有重写hashCode()!这会导致问题
// @Override
// public int hashCode() {
// return name.hashCode();
// }
}

/**
* 正确示例:同时重写equals()和hashCode()
*/
public class GoodPerson {
private String name;

public GoodPerson(String name) {
this.name = name;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
GoodPerson person = (GoodPerson) obj;
return name.equals(person.name);
}

@Override
public int hashCode() {
return name.hashCode(); // 必须重写!
}
}

toString()方法

toString()方法返回对象的字符串表示,对于调试和日志记录非常重要。

🎯 toString()最佳实践

public class Product {
private String id;
private String name;
private double price;
private int quantity;
private Category category;

public Product(String id, String name, double price, int quantity, Category category) {
this.id = id;
this.name = name;
this.price = price;
this.quantity = quantity;
this.category = category;
}

// 1. 简单版本的toString()
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
", quantity=" + quantity +
'}';
}

// 2. 详细版本的toString()
public String toDetailedString() {
return String.format("Product详情:\n" +
" ID: %s\n" +
" 名称: %s\n" +
" 价格: %.2f元\n" +
" 库存: %d件\n" +
" 分类: %s\n" +
" 总价值: %.2f元",
id, name, price, quantity,
category.getName(), price * quantity);
}

// 3. JSON格式的toString()
public String toJsonString() {
return String.format("{\"id\":\"%s\",\"name\":\"%s\",\"price\":%.2f,\"quantity\":%d,\"category\":\"%s\"}",
id, name, price, quantity, category.getName());
}

// 枚举示例
public enum Category {
ELECTRONICS("电子产品"),
CLOTHING("服装"),
FOOD("食品"),
BOOKS("图书");

private String displayName;

Category(String displayName) {
this.displayName = displayName;
}

public String getName() {
return displayName;
}
}
}

🧪 toString()使用示例

public class ToStringDemo {
public static void main(String[] args) {
Product laptop = new Product("P001", "MacBook Pro", 12999.99, 5, Product.Category.ELECTRONICS);

// 默认toString()
System.out.println("默认toString():");
System.out.println(laptop);

System.out.println("\n详细toString():");
System.out.println(laptop.toDetailedString());

System.out.println("\nJSON格式toString():");
System.out.println(laptop.toJsonString());

// 在集合中使用
List<Product> products = Arrays.asList(
new Product("P001", "MacBook Pro", 12999.99, 5, Product.Category.ELECTRONICS),
new Product("P002", "iPhone 15", 7999.00, 10, Product.Category.ELECTRONICS),
new Product("P003", "Java编程思想", 89.90, 20, Product.Category.BOOKS)
);

System.out.println("\n产品列表:");
for (Product product : products) {
System.out.println(product); // 自动调用toString()
}
}
}

clone()方法

clone()方法用于创建对象的副本,需要注意浅拷贝和深拷贝的区别。

🔄 浅拷贝 vs 深拷贝

/**
* 浅拷贝示例
*/
public class ShallowCopyExample implements Cloneable {
private String name;
private int[] scores; // 引用类型

public ShallowCopyExample(String name, int[] scores) {
this.name = name;
this.scores = scores;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}

// Getter和Setter
public String getName() { return name; }
public int[] getScores() { return scores; }
public void setScore(int index, int value) { scores[index] = value; }
}

/**
* 深拷贝示例
*/
public class DeepCopyExample implements Cloneable {
private String name;
private int[] scores; // 引用类型

public DeepCopyExample(String name, int[] scores) {
this.name = name;
this.scores = scores;
}

@Override
protected Object clone() throws CloneNotSupportedException {
DeepCopyExample cloned = (DeepCopyExample) super.clone();
cloned.scores = scores.clone(); // 深拷贝引用类型
return cloned;
}

// Getter和Setter
public String getName() { return name; }
public int[] getScores() { return scores; }
public void setScore(int index, int value) { scores[index] = value; }
}

🧪 克隆测试对比

public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
// 浅拷贝测试
System.out.println("=== 浅拷贝测试 ===");
int[] scores1 = {90, 85, 95};
ShallowCopyExample original1 = new ShallowCopyExample("张三", scores1);
ShallowCopyExample shallowCopy = (ShallowCopyExample) original1.clone();

System.out.println("原始对象分数: " + Arrays.toString(original1.getScores()));
System.out.println("浅拷贝对象分数: " + Arrays.toString(shallowCopy.getScores()));

// 修改拷贝对象的分数
shallowCopy.setScore(0, 100);
System.out.println("修改后 - 原始对象分数: " + Arrays.toString(original1.getScores()));
System.out.println("修改后 - 浅拷贝对象分数: " + Arrays.toString(shallowCopy.getScores()));

// 深拷贝测试
System.out.println("\n=== 深拷贝测试 ===");
int[] scores2 = {90, 85, 95};
DeepCopyExample original2 = new DeepCopyExample("李四", scores2);
DeepCopyExample deepCopy = (DeepCopyExample) original2.clone();

System.out.println("原始对象分数: " + Arrays.toString(original2.getScores()));
System.out.println("深拷贝对象分数: " + Arrays.toString(deepCopy.getScores()));

// 修改拷贝对象的分数
deepCopy.setScore(0, 100);
System.out.println("修改后 - 原始对象分数: " + Arrays.toString(original2.getScores()));
System.out.println("修改后 - 深拷贝对象分数: " + Arrays.toString(deepCopy.getScores()));
}
}

🔐 访问控制与权限

四种访问修饰符

Java提供了四种访问修饰符来控制类、方法和变量的访问权限。

修饰符本类同包子类所有类
public
protected✅ ✅
default
private

🎯 访问修饰符示例

package com.example.access;

public class AccessControlDemo {

// 1. public: 可以被任何类访问
public String publicVar = "public变量";

// 2. protected: 可以被同包类和子类访问
protected String protectedVar = "protected变量";

// 3. default: 只能被同包类访问
String defaultVar = "default变量";

// 4. private: 只能被本类访问
private String privateVar = "private变量";

// 构造方法的访问控制
public AccessControlDemo() {
System.out.println("public构造方法");
}

protected AccessControlDemo(String param) {
System.out.println("protected构造方法:" + param);
}

AccessControlDemo(int param) { // default构造方法
System.out.println("default构造方法:" + param);
}

private AccessControlDemo(boolean param) {
System.out.println("private构造方法:" + param);
}

// 方法的访问控制
public void publicMethod() {
System.out.println("public方法");
System.out.println("访问private变量:" + privateVar);
}

protected void protectedMethod() {
System.out.println("protected方法");
}

void defaultMethod() {
System.out.println("default方法");
}

private void privateMethod() {
System.out.println("private方法");
}

// 提供访问私有变量方法
public String getPrivateVar() {
return privateVar;
}

public void setPrivateVar(String privateVar) {
this.privateVar = privateVar;
}
}

🏠 同包类访问测试

package com.example.access;

// 同包类
public class SamePackageClass {

public void testAccess() {
AccessControlDemo demo = new AccessControlDemo();

System.out.println("同包类访问测试:");

// ✅ 可以访问public成员
System.out.println("publicVar: " + demo.publicVar);
demo.publicMethod();

// ✅ 可以访问protected成员(同包)
System.out.println("protectedVar: " + demo.protectedVar);
demo.protectedMethod();

// ✅ 可以访问default成员(同包)
System.out.println("defaultVar: " + demo.defaultVar);
demo.defaultMethod();

// ❌ 不能访问private成员
// System.out.println("privateVar: " + demo.privateVar);
// demo.privateMethod();

// ✅ 可以通过public方法间接访问private成员
System.out.println("通过public方法访问privateVar: " + demo.getPrivateVar());
}
}

🧬 不同包子类访问测试

package com.example.other;

import com.example.access.AccessControlDemo;

// 不同包子类
public class DifferentPackageSubclass extends AccessControlDemo {

public DifferentPackageSubclass() {
super("子类构造"); // 调用protected构造方法
}

public void testAccess() {
AccessControlDemo demo = new DifferentPackageSubclass();

System.out.println("不同包子类访问测试:");

// ✅ 可以访问public成员
System.out.println("publicVar: " + demo.publicVar);
demo.publicMethod();

// ✅ 可以访问protected成员(子类)
System.out.println("protectedVar: " + demo.protectedVar);
demo.protectedMethod();

// ❌ 不能访问default成员(不同包)
// System.out.println("defaultVar: " + demo.defaultVar);
// demo.defaultMethod();

// ❌ 不能访问private成员
// System.out.println("privateVar: " + demo.privateVar);
// demo.privateMethod();
}

// 重写protected方法
@Override
protected void protectedMethod() {
System.out.println("子类重写的protected方法");
super.protectedMethod(); // 调用父类方法
}
}

封装的最佳实践

🛡️ 封装设计原则

/**
* 银行账户类 - 封装的最佳实践示例
*/
public class BankAccount {

// 1. 所有字段都应该是private的
private String accountNumber;
private double balance;
private String ownerName;
private boolean isActive;
private List<String> transactionHistory;

// 2. 静态常量使用public static final
public static final double MIN_BALANCE = 0.0;
public static final double MAX_DAILY_WITHDRAWAL = 50000.0;

// 3. 构造方法进行数据验证
public BankAccount(String accountNumber, String ownerName, double initialBalance) {
validateAccountNumber(accountNumber);
validateOwnerName(ownerName);
validateInitialBalance(initialBalance);

this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = initialBalance;
this.isActive = true;
this.transactionHistory = new ArrayList<>();
addTransaction("开户", initialBalance);
}

// 4. 业务方法包含验证逻辑
public boolean deposit(double amount) {
if (!isActive) {
System.out.println("账户已被冻结,无法存款");
return false;
}

if (amount <= 0) {
System.out.println("存款金额必须大于0");
return false;
}

balance += amount;
addTransaction("存款", amount);
System.out.println("存款成功,当前余额:" + balance);
return true;
}

public boolean withdraw(double amount) {
if (!isActive) {
System.out.println("账户已被冻结,无法取款");
return false;
}

if (amount <= 0) {
System.out.println("取款金额必须大于0");
return false;
}

if (amount > balance) {
System.out.println("余额不足,无法取款");
return false;
}

if (amount > MAX_DAILY_WITHDRAWAL) {
System.out.println("单日取款金额超过限额:" + MAX_DAILY_WITHDRAWAL);
return false;
}

balance -= amount;
addTransaction("取款", -amount);
System.out.println("取款成功,当前余额:" + balance);
return true;
}

// 5. 只提供必要的getter方法
public String getAccountNumber() { return accountNumber; }

public double getBalance() { return balance; }

public String getOwnerName() { return ownerName; }

public boolean isActive() { return isActive; }

// 6. 提供受控的setter方法
public void setActive(boolean active) {
if (!active && this.isActive) {
addTransaction("账户冻结", 0);
} else if (active && !this.isActive) {
addTransaction("账户激活", 0);
}
this.isActive = active;
}

public void setOwnerName(String newName) {
if (newName == null || newName.trim().isEmpty()) {
throw new IllegalArgumentException("户主姓名不能为空");
}
String oldName = this.ownerName;
this.ownerName = newName.trim();
addTransaction("户主变更: " + oldName + " -> " + newName, 0);
}

// 7. 不提供setter给敏感字段(如账户号)
// public void setAccountNumber(String accountNumber) {
// // 不允许修改账户号!
// }

// 8. 提供只读的业务信息
public List<String> getTransactionHistory() {
return new ArrayList<>(transactionHistory); // 返回副本,保护原始数据
}

public String getAccountSummary() {
return String.format("账户摘要\n" +
"账户号: %s\n" +
"户主: %s\n" +
"余额: %.2f元\n" +
"状态: %s\n" +
"交易记录数: %d",
accountNumber, ownerName, balance,
isActive ? "正常" : "冻结",
transactionHistory.size());
}

// 9. 私有的验证方法
private void validateAccountNumber(String accountNumber) {
if (accountNumber == null || !accountNumber.matches("\\d{16}")) {
throw new IllegalArgumentException("账户号必须是16位数字");
}
}

private void validateOwnerName(String ownerName) {
if (ownerName == null || ownerName.trim().isEmpty()) {
throw new IllegalArgumentException("户主姓名不能为空");
}
}

private void validateInitialBalance(double initialBalance) {
if (initialBalance < MIN_BALANCE) {
throw new IllegalArgumentException("初始余额不能小于:" + MIN_BALANCE);
}
}

private void addTransaction(String type, double amount) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
transactionHistory.add(String.format("[%s] %s: %.2f元", timestamp, type, amount));
}

@Override
public String toString() {
return String.format("BankAccount{accountNumber='%s', ownerName='%s', balance=%.2f, isActive=%s}",
accountNumber, ownerName, balance, isActive);
}
}

🔧 方法重写与重载

方法重写(Override)

方法重写发生在子类中,子类重新实现父类已有的方法。

🎯 重写规则

  • 方法签名必须相同(方法名、参数列表)
  • 返回类型可以是父类方法的子类型(协变返回类型)
  • 访问权限不能比父类更严格
  • 抛出的异常不能比父类更宽泛
  • @Override注解帮助编译器检查重写是否正确
// 父类
public class Animal {
protected String name;
protected int age;

public Animal(String name, int age) {
this.name = name;
this.age = age;
}

public void makeSound() {
System.out.println(name + "发出声音");
}

public void eat() {
System.out.println(name + "正在吃东西");
}

public String getInfo() {
return String.format("动物{name='%s', age=%d}", name, age);
}

protected void sleep() {
System.out.println(name + "正在睡觉");
}
}

// 子类 - 正确的重写示例
public class Dog extends Animal {
private String breed;

public Dog(String name, int age, String breed) {
super(name, age);
this.breed = breed;
}

// ✅ 正确重写:方法签名相同,访问权限更宽松
@Override
public void makeSound() {
System.out.println(breed + "犬" + name + "在汪汪叫");
}

// ✅ 正确重写:返回类型是String(相同)
@Override
public String getInfo() {
return String.format("狗{name='%s', breed='%s', age=%d}", name, breed, age);
}

// ✅ 正确重写:可以调用父类方法
@Override
public void eat() {
System.out.println(breed + "犬" + name + "正在吃狗粮");
super.eat(); // 调用父类的eat方法
}

// ❌ 错误示例:访问权限更严格
// @Override
// private void makeSound() {
// System.out.println("错误:访问权限不能比父类更严格");
// }

// ❌ 错误示例:参数列表不同(这是重载,不是重写)
// @Override
// public void makeSound(String sound) {
// System.out.println(sound);
// }

// ✅ 正确重写:protected方法可以重写为public
@Override
public void sleep() {
System.out.println(breed + "犬" + name + "在狗窝里睡觉");
super.sleep();
}
}

🧪 重写示例测试

public class OverrideTest {
public static void main(String[] args) {
Animal animal = new Animal("动物", 5);
Dog dog = new Dog("小黑", 3, "拉布拉多");

System.out.println("=== 父类对象调用 ===");
animal.makeSound();
animal.eat();
System.out.println(animal.getInfo());

System.out.println("\n=== 子类对象调用 ===");
dog.makeSound(); // 调用子类重写的方法
dog.eat(); // 调用子类重写的方法
System.out.println(dog.getInfo()); // 调用子类重写的方法

System.out.println("\n=== 多态调用 ===");
Animal polyDog = new Dog("小白", 2, "柯基");
polyDog.makeSound(); // 运行时调用子类的方法
polyDog.eat(); // 运行时调用子类的方法
System.out.println(polyDog.getInfo()); // 运行时调用子类的方法
}
}

方法重载(Overload)

方法重载发生在同一个类中,方法名相同但参数列表不同。

🎯 重载规则

  • 方法名必须相同
  • 参数列表必须不同(参数类型、数量或顺序不同)
  • 返回类型可以不同
  • 访问修饰符可以不同
  • 可以抛出不同的异常
public class Calculator {

// 1. 基本的重载:不同参数数量
public int add(int a, int b) {
System.out.println("调用两个整数相加");
return a + b;
}

public int add(int a, int b, int c) {
System.out.println("调用三个整数相加");
return a + b + c;
}

// 2. 参数类型不同的重载
public double add(double a, double b) {
System.out.println("调用两个浮点数相加");
return a + b;
}

public double add(int a, double b) {
System.out.println("调用整数和浮点数相加");
return a + b;
}

public double add(double a, int b) {
System.out.println("调用浮点数和整数相加");
return a + b;
}

// 3. 可变参数重载
public int add(int... numbers) {
System.out.println("调用可变整数参数相加");
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}

public double add(double... numbers) {
System.out.println("调用可变浮点数参数相加");
double sum = 0;
for (double num : numbers) {
sum += num;
}
return sum;
}

// 4. 不同参数类型的组合
public String add(String str1, String str2) {
System.out.println("调用两个字符串相加");
return str1 + str2;
}

public String add(String str, int num) {
System.out.println("调用字符串和整数相加");
return str + num;
}

// 5. 引用类型重载
public List<Integer> add(List<Integer> list1, List<Integer> list2) {
System.out.println("调用两个列表相加");
List<Integer> result = new ArrayList<>(list1);
result.addAll(list2);
return result;
}

// 6. 数组参数重载
public int sum(int[] array) {
System.out.println("调用整数数组求和");
int total = 0;
for (int num : array) {
total += num;
}
return total;
}

public double sum(double[] array) {
System.out.println("调用浮点数数组求和");
double total = 0;
for (double num : array) {
total += num;
}
return total;
}
}

🧪 重载示例测试

public class OverloadTest {
public static void main(String[] args) {
Calculator calc = new Calculator();

System.out.println("=== 基本重载测试 ===");
System.out.println("2 + 3 = " + calc.add(2, 3)); // 两个整数
System.out.println("2 + 3 + 4 = " + calc.add(2, 3, 4)); // 三个整数
System.out.println("2.5 + 3.5 = " + calc.add(2.5, 3.5)); // 两个浮点数
System.out.println("2 + 3.5 = " + calc.add(2, 3.5)); // 整数+浮点数
System.out.println("2.5 + 3 = " + calc.add(2.5, 3)); // 浮点数+整数

System.out.println("\n=== 可变参数重载测试 ===");
System.out.println("1 + 2 + 3 + 4 = " + calc.add(1, 2, 3, 4)); // 可变整数参数
System.out.println("1.1 + 2.2 + 3.3 = " + calc.add(1.1, 2.2, 3.3)); // 可变浮点数参数

System.out.println("\n=== 其他类型重载测试 ===");
System.out.println("\"Hello\" + \"World\" = " + calc.add("Hello", "World")); // 字符串
System.out.println("\"Num\" + 42 = " + calc.add("Num", 42)); // 字符串+整数

System.out.println("\n=== 引用类型重载测试 ===");
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(4, 5, 6);
System.out.println("列表相加: " + calc.add(list1, list2));

int[] intArray = {1, 2, 3, 4, 5};
double[] doubleArray = {1.1, 2.2, 3.3};
System.out.println("整数数组求和: " + calc.sum(intArray));
System.out.println("浮点数数组求和: " + calc.sum(doubleArray));

// 特殊情况:重载歧义
// System.out.println(calc.add(null, null)); // 编译错误:有歧义
System.out.println("解决重载歧义: " + calc.add((String) null, (String) null));
}
}

⚠️ 重载注意事项

public class OverloadPitfalls {

// ❌ 容易产生歧义的重载
public void method(Object obj) {
System.out.println("Object参数");
}

public void method(String str) {
System.out.println("String参数");
}

public void method(Integer num) {
System.out.println("Integer参数");
}

// 调用测试
public void testAmbiguity() {
method("Hello"); // 调用String版本
method(123); // 调用Integer版本
method(new Object()); // 调用Object版本

// method(null); // 编译错误:有歧义

// 解决方法1:强制类型转换
method((String) null); // 明确调用String版本
method((Integer) null); // 明确调用Integer版本

// 解决方法2:提供更具体的方法
methodNullSafe(); // 专门处理null的方法
}

// 提供专门处理null的方法
public void methodNullSafe() {
System.out.println("处理null参数");
}
}

🧬 继承体系

继承的概念与特性

继承是面向对象编程的三大特性之一,允许子类继承父类的属性和方法。

🎯 继承的核心特性

  1. 代码复用:子类继承父类,避免重复代码
  2. 扩展性:子类可以添加新特性
  3. 多态性:父类引用可以指向子类对象
  4. 层次结构:形成类的层次体系

🧬 继承示例

// 父类:员工
public class Employee {
protected String name;
protected int age;
protected double salary;
protected String department;

public Employee(String name, int age, double salary, String department) {
this.name = name;
this.age = age;
this.salary = salary;
this.department = department;
}

// 基本方法
public void work() {
System.out.println(name + "正在工作中...");
}

public void takeBreak() {
System.out.println(name + "正在休息");
}

public void displayInfo() {
System.out.println("员工信息:");
System.out.println(" 姓名: " + name);
System.out.println(" 年龄: " + age);
System.out.println(" 薪资: " + salary);
System.out.println(" 部门: " + department);
}

// 计算年终奖
public double calculateBonus() {
return salary * 0.1; // 默认10%的年终奖
}

// Getter和Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }

public double getSalary() { return salary; }

public void setSalary(double salary) {
if (salary > 0) {
this.salary = salary;
}
}
}

// 子类:经理
public class Manager extends Employee {
private double bonus;
private int teamSize;
private String[] projects;

public Manager(String name, int age, double salary, String department,
double bonus, int teamSize) {
super(name, age, salary, department); // 调用父类构造方法
this.bonus = bonus;
this.teamSize = teamSize;
this.projects = new String[10]; // 管理最多10个项目
}

// 重写父类方法
@Override
public void work() {
System.out.println("经理" + name + "正在管理团队和项目...");
manageTeam();
}

@Override
public double calculateBonus() {
// 经理的年终奖 = 基本年终奖 + 团队奖金
return super.calculateBonus() + (teamSize * 1000) + bonus;
}

@Override
public void displayInfo() {
super.displayInfo(); // 调用父类方法
System.out.println(" 职位: 经理");
System.out.println(" 团队规模: " + teamSize + "人");
System.out.println(" 固定奖金: " + bonus);
System.out.println(" 管理项目数: " + getCurrentProjectCount());
}

// 子类特有的方法
public void manageTeam() {
System.out.println("正在管理" + teamSize + "人的团队");
}

public void addProject(String projectName) {
for (int i = 0; i < projects.length; i++) {
if (projects[i] == null) {
projects[i] = projectName;
System.out.println("成功添加项目: " + projectName);
return;
}
}
System.out.println("项目已满,无法添加: " + projectName);
}

public void removeProject(String projectName) {
for (int i = 0; i < projects.length; i++) {
if (projectName.equals(projects[i])) {
projects[i] = null;
System.out.println("成功移除项目: " + projectName);
return;
}
}
System.out.println("未找到项目: " + projectName);
}

public void listProjects() {
System.out.println("当前管理项目:");
for (int i = 0; i < projects.length; i++) {
if (projects[i] != null) {
System.out.println(" " + (i + 1) + ". " + projects[i]);
}
}
}

private int getCurrentProjectCount() {
int count = 0;
for (String project : projects) {
if (project != null) {
count++;
}
}
return count;
}

// Getter和Setter
public double getBonus() { return bonus; }
public void setBonus(double bonus) { this.bonus = bonus; }

public int getTeamSize() { return teamSize; }
public void setTeamSize(int teamSize) { this.teamSize = teamSize; }
}

// 子类:程序员
public class Programmer extends Employee {
private String[] programmingLanguages;
private int experienceYears;
private String level;

public Programmer(String name, int age, double salary, String department,
String[] languages, int experienceYears) {
super(name, age, salary, department);
this.programmingLanguages = languages.clone();
this.experienceYears = experienceYears;
this.level = determineLevel(experienceYears);
}

@Override
public void work() {
System.out.println("程序员" + name + "正在编写代码...");
codeReview();
}

@Override
public void displayInfo() {
super.displayInfo();
System.out.println(" 职位: 程序员");
System.out.println(" 经验: " + experienceYears + "年");
System.out.println(" 级别: " + level);
System.out.println(" 编程语言: " + String.join(", ", programmingLanguages));
}

@Override
public double calculateBonus() {
// 程序员的年终奖基于经验和技能
double baseBonus = super.calculateBonus();
double experienceBonus = experienceYears * 500;
double skillBonus = programmingLanguages.length * 200;
return baseBonus + experienceBonus + skillBonus;
}

// 程序员特有方法
public void codeReview() {
System.out.println(name + "正在进行代码审查");
}

public void learnNewLanguage(String language) {
for (String lang : programmingLanguages) {
if (lang.equals(language)) {
System.out.println("已经掌握" + language + "语言");
return;
}
}

// 扩展数组
String[] newLanguages = new String[programmingLanguages.length + 1];
System.arraycopy(programmingLanguages, 0, newLanguages, 0, programmingLanguages.length);
newLanguages[programmingLanguages.length] = language;
programmingLanguages = newLanguages;

System.out.println(name + "学会了新语言: " + language);
}

public void promote() {
experienceYears++;
level = determineLevel(experienceYears);
System.out.println("恭喜" + name + "升级为" + level);
}

private String determineLevel(int years) {
if (years >= 10) return "高级程序员";
if (years >= 5) return "中级程序员";
if (years >= 2) return "初级程序员";
return "实习生";
}
}

super关键字

super关键字用于访问父类的成员和方法。

🎯 super关键字使用场景

public class SuperKeywordDemo {

// 父类
static class Vehicle {
protected String brand;
protected String model;
protected int year;

public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
System.out.println("Vehicle构造方法被调用");
}

public void start() {
System.out.println("车辆启动");
}

public void displayInfo() {
System.out.println("车辆信息: " + year + " " + brand + " " + model);
}
}

// 子类
static class Car extends Vehicle {
private int numberOfDoors;
private String fuelType;

// 1. 调用父类构造方法
public Car(String brand, String model, int year, int doors, String fuel) {
super(brand, model, year); // 必须是第一行
this.numberOfDoors = doors;
this.fuelType = fuel;
System.out.println("Car构造方法被调用");
}

// 2. 调用父类方法
@Override
public void displayInfo() {
super.displayInfo(); // 调用父类的displayInfo方法
System.out.println("车门数: " + numberOfDoors);
System.out.println("燃料类型: " + fuelType);
}

// 3. 调用并扩展父类方法
@Override
public void start() {
super.start(); // 调用父类的start方法
System.out.println("汽车引擎启动,准备行驶");
}

// 4. 访问父类的字段
public void upgradeModel(String newModel) {
super.model = newModel; // 访问父类的protected字段
System.out.println("车型升级为: " + newModel);
}

// 5. 在子类中访问父类方法的完整路径
public void compareWithSuper() {
System.out.println("当前对象: " + this);
System.out.println("父类部分: " + super);
}
}

// 孙子类
static class ElectricCar extends Car {
private int batteryCapacity;
private int range;

public ElectricCar(String brand, String model, int year, int doors,
String fuel, int batteryCapacity, int range) {
super(brand, model, year, doors, fuel);
this.batteryCapacity = batteryCapacity;
this.range = range;
System.out.println("ElectricCar构造方法被调用");
}

@Override
public void start() {
// 可以调用祖父类的方法
super.start(); // 调用Car的start方法,Car内部会调用Vehicle的start方法

System.out.println("电动汽车静音启动");
System.out.println("电池容量: " + batteryCapacity + "kWh");
System.out.println("续航里程: " + range + "km");
}

public void chargeBattery() {
System.out.println("正在充电...");
System.out.println("充电后续航: " + range + "km");
}
}
}

🎯 实战应用场景

实际项目中的类设计

🏦 银行管理系统

/**
* 银行管理系统 - 面向对象设计示例
*/
public class BankingSystemDemo {

// 抽象基类:账户
public abstract class Account {
protected String accountNumber;
protected String accountHolder;
protected double balance;
protected LocalDate openingDate;
protected List<Transaction> transactions;

public Account(String accountNumber, String accountHolder, double initialBalance) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.balance = initialBalance;
this.openingDate = LocalDate.now();
this.transactions = new ArrayList<>();
addTransaction(new Transaction("开户", initialBalance, TransactionType.DEPOSIT));
}

// 抽象方法:子类必须实现
public abstract double calculateInterest();
public abstract boolean canWithdraw(double amount);
public abstract String getAccountType();

// 具体方法
public boolean deposit(double amount) {
if (amount <= 0) return false;

balance += amount;
addTransaction(new Transaction("存款", amount, TransactionType.DEPOSIT));
return true;
}

public boolean withdraw(double amount) {
if (amount <= 0 || !canWithdraw(amount)) return false;

balance -= amount;
addTransaction(new Transaction("取款", amount, TransactionType.WITHDRAWAL));
return true;
}

protected void addTransaction(Transaction transaction) {
transactions.add(transaction);
}

public void printStatement() {
System.out.println("=== " + getAccountType() + " 对账单 ===");
System.out.println("账户号: " + accountNumber);
System.out.println("户主: " + accountHolder);
System.out.println("开户日期: " + openingDate);
System.out.println("当前余额: " + balance);
System.out.println("交易记录:");

for (Transaction transaction : transactions) {
System.out.println(" " + transaction);
}
}

// Getter方法
public String getAccountNumber() { return accountNumber; }
public String getAccountHolder() { return accountHolder; }
public double getBalance() { return balance; }
}

// 具体子类:储蓄账户
public class SavingsAccount extends Account {
private double interestRate;
private double minimumBalance;

public SavingsAccount(String accountNumber, String accountHolder,
double initialBalance, double interestRate) {
super(accountNumber, accountHolder, initialBalance);
this.interestRate = interestRate;
this.minimumBalance = 100.0;
}

@Override
public double calculateInterest() {
return balance * interestRate / 12; // 月利息
}

@Override
public boolean canWithdraw(double amount) {
return balance - amount >= minimumBalance;
}

@Override
public String getAccountType() {
return "储蓄账户";
}

public void applyMonthlyInterest() {
double interest = calculateInterest();
if (interest > 0) {
balance += interest;
addTransaction(new Transaction("利息", interest, TransactionType.INTEREST));
System.out.println("利息已添加: " + interest);
}
}
}

// 具体子类:支票账户
public class CheckingAccount extends Account {
private double overdraftLimit;
private double monthlyFee;
private int freeTransactionsPerMonth;
private int transactionCount;

public CheckingAccount(String accountNumber, String accountHolder,
double initialBalance, double overdraftLimit) {
super(accountNumber, accountHolder, initialBalance);
this.overdraftLimit = overdraftLimit;
this.monthlyFee = 10.0;
this.freeTransactionsPerMonth = 10;
this.transactionCount = 0;
}

@Override
public double calculateInterest() {
return 0; // 支票账户通常没有利息
}

@Override
public boolean canWithdraw(double amount) {
return balance + overdraftLimit >= amount;
}

@Override
public String getAccountType() {
return "支票账户";
}

@Override
public boolean withdraw(double amount) {
if (super.withdraw(amount)) {
transactionCount++;
if (transactionCount > freeTransactionsPerMonth) {
balance -= 1.0; // 超出免费交易次数的手续费
addTransaction(new Transaction("交易手续费", 1.0, TransactionType.FEE));
}
return true;
}
return false;
}

public void applyMonthlyFee() {
balance -= monthlyFee;
addTransaction(new Transaction("月费", monthlyFee, TransactionType.FEE));
transactionCount = 0; // 重置交易计数
}
}

// 交易记录类
public class Transaction {
private String description;
private double amount;
private TransactionType type;
private LocalDateTime timestamp;

public Transaction(String description, double amount, TransactionType type) {
this.description = description;
this.amount = amount;
this.type = type;
this.timestamp = LocalDateTime.now();
}

@Override
public String toString() {
return String.format("[%s] %s: %s%.2f元",
timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
description,
type == TransactionType.DEPOSIT || type == TransactionType.INTEREST ? "+" : "-",
Math.abs(amount));
}
}

public enum TransactionType {
DEPOSIT, WITHDRAWAL, INTEREST, FEE
}

// 银行类 - 管理多个账户
public class Bank {
private String bankName;
private Map<String, Account> accounts;
private List<Customer> customers;

public Bank(String bankName) {
this.bankName = bankName;
this.accounts = new HashMap<>();
this.customers = new ArrayList<>();
}

public Account createAccount(String accountType, String accountNumber,
String accountHolder, double initialBalance) {
Account account;

switch (accountType.toLowerCase()) {
case "savings":
account = new SavingsAccount(accountNumber, accountHolder,
initialBalance, 0.025);
break;
case "checking":
account = new CheckingAccount(accountNumber, accountHolder,
initialBalance, 1000.0);
break;
default:
throw new IllegalArgumentException("不支持的账户类型: " + accountType);
}

accounts.put(accountNumber, account);
customers.add(new Customer(accountHolder));

System.out.println("成功创建" + account.getAccountType() + ": " + accountNumber);
return account;
}

public Account findAccount(String accountNumber) {
return accounts.get(accountNumber);
}

public void processMonthlyMaintenance() {
System.out.println("=== 处理月度维护 ===");

for (Account account : accounts.values()) {
if (account instanceof SavingsAccount) {
((SavingsAccount) account).applyMonthlyInterest();
} else if (account instanceof CheckingAccount) {
((CheckingAccount) account).applyMonthlyFee();
}
}
}

public void printAllAccounts() {
System.out.println("=== " + bankName + " 所有账户 ===");
for (Account account : accounts.values()) {
account.printStatement();
System.out.println();
}
}
}

// 客户类
public class Customer {
private String name;
private List<String> accountNumbers;

public Customer(String name) {
this.name = name;
this.accountNumbers = new ArrayList<>();
}

public void addAccount(String accountNumber) {
accountNumbers.add(accountNumber);
}

@Override
public String toString() {
return String.format("Customer{name='%s', accountCount=%d}",
name, accountNumbers.size());
}
}
}

使用示例

public class BankingSystemUsage {
public static void main(String[] args) {
// 创建银行
BankingSystemDemo.Bank bank = new BankingSystemDemo.Bank("Java银行");

// 创建不同类型的账户
BankingSystemDemo.Account savings1 = bank.createAccount("savings", "SV001", "张三", 5000.0);
BankingSystemDemo.Account checking1 = bank.createAccount("checking", "CK001", "李四", 2000.0);
BankingSystemDemo.Account savings2 = bank.createAccount("savings", "SV002", "王五", 10000.0);

// 进行一些交易
System.out.println("\n=== 进行交易 ===");
savings1.deposit(1000.0);
savings1.withdraw(500.0);

checking1.withdraw(2500.0); // 使用透支
checking1.withdraw(100.0);
checking1.withdraw(100.0);
checking1.withdraw(100.0); // 超出免费交易次数

savings2.deposit(2000.0);

// 处理月度维护
System.out.println("\n=== 月度维护 ===");
bank.processMonthlyMaintenance();

// 打印所有账户对账单
bank.printAllAccounts();
}
}

🎉 恭喜!你已经完成了Java面向对象编程的完整学习! 这份指南涵盖了类与对象的所有核心概念,从基础语法到高级设计模式,从理论原理到实战应用。现在你具备了设计高质量Java类的能力!


❓ 常见面试题

🎯 面试题1:类与对象的关系

问题:什么是类?什么是对象?它们之间有什么关系?

回答要点

  • 是模板/蓝图,对象是具体实例
  • 类定义属性和行为,对象拥有具体的状态
  • 对象通过new关键字从类创建
  • 一个类可以创建多个对象

🎯 面试题2:OOP三大特性

问题:面向对象编程的三大特性是什么?请举例说明。

回答要点

  1. 封装:隐藏内部实现,暴露必要接口
  2. 继承:子类继承父类,实现代码复用
  3. 多态:同一接口,不同实现

🎯 面试题3:equals()和hashCode()

问题:为什么重写equals()必须重写hashCode()?

回答要点

  • 合同规定:相等的对象必须有相同的哈希码
  • 集合框架:HashMap、HashSet等依赖hashCode
  • 性能考虑:hashCode用于快速查找,equals用于精确比较

🎯 面试题4:重写vs重载

问题:方法重写和方法重载有什么区别?

回答要点

  • 重写:子类重新实现父类方法,运行时多态
  • 重载:同一类中同名不同参数,编译时确定
  • 重写规则:方法签名相同,访问权限不能更严格
  • 重载规则:方法名相同,参数列表不同

📚 学习总结

🎯 核心要点回顾

  1. 类与对象:类是模板,对象是实例
  2. OOP三大特性:封装、继承、多态
  3. Object类:所有类的父类,提供基础方法
  4. 访问控制:public、protected、default、private
  5. 方法重写与重载:实现多态和功能扩展
  6. 继承体系:建立类的层次结构
  7. 封装设计:保护数据,提供安全接口

🚀 最佳实践

  • 遵循SOLID原则设计类
  • 合理使用访问修饰符控制访问权限
  • 重写equals()时重写hashCode()
  • 使用@Override注解确保正确重写
  • 优先使用组合而非继承
  • 设计良好的接口和抽象类
  • ✅ **提供清晰的toString()**方法
  • ✅ **正确实现clone()**方法

🔥 进阶学习路径

  1. 设计模式:学习常用的面向对象设计模式
  2. UML建模:掌握类图的设计和阅读
  3. 框架源码:阅读Spring、MyBatis等框架源码
  4. 性能优化:学习对象创建和内存管理
  5. 并发编程:掌握多线程环境下的对象设计

💡 最后建议:面向对象编程是一种思维模式,需要通过大量实践来掌握。多设计类,多写代码,多思考设计原则,逐步形成良好的编程习惯!

🎊 恭喜你完成了Java面向对象编程的学习之旅! 现在去设计你自己的类吧!