🚀 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)。
✅ 优势分析
- 🧩 模块化:把代码像乐高一样拆分和组装,结构清晰
- ♻️ 复用性:一份蓝图(类)可以造出无数个对象,避免重复代码
- 🛠️ 易维护:想修改所有猫的行为?直接改蓝图(类)就行了
- 🔒 安全性:通过封装保护数据,防止意外修改
- 🎭 灵活性:通过多态实现不同对象的统一处理
- 📈 扩展性:通过继承轻松扩展功能,不影响现有代码
🎯 实际应用场景
// 用户管理系统示例
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参数");
}
}
🧬 继承体系
继承的概念与特性
继承是面向对象编程的三大特性之一,允许子类继承父类的属性和方法。
🎯 继承的核心特性
- 代码复用:子类继承父类,避免重复代码
- 扩展性:子类可以添加新特性
- 多态性:父类引用可以指向子类对象
- 层次结构:形成类的层次体系
🧬 继承示例
// 父类:员工
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三大特性
问题:面向对象编程的三大特性是什么?请举例说明。
回答要点:
- 封装:隐藏内部实现,暴露必要接口
- 继承:子类继承父类,实现代码复用
- 多态:同一接口,不同实现
🎯 面试题3:equals()和hashCode()
问题:为什么重写equals()必须重写hashCode()?
回答要点:
- 合同规定:相等的对象必须有相同的哈希码
- 集合框架:HashMap、HashSet等依赖hashCode
- 性能考虑:hashCode用于快速查找,equals用于精确比较
🎯 面试题4:重写vs重载
问题:方法重写和方法重载有什么区别?
回答要点:
- 重写:子类重新实现父类方法,运行时多态
- 重载:同一类中同名不同参数,编译时确定
- 重写规则:方法签名相同,访问权限不能更严格
- 重载规则:方法名相同,参数列表不同
📚 学习总结
🎯 核心要点回顾
- 类与对象:类是模板,对象是实例
- OOP三大特性:封装、继承、多态
- Object类:所有类的父类,提供基础方法
- 访问控制:public、protected、default、private
- 方法重写与重载:实现多态和功能扩展
- 继承体系:建立类的层次结构
- 封装设计:保护数据,提供安全接口
🚀 最佳实践
- ✅ 遵循SOLID原则设计类
- ✅ 合理使用访问修饰符控制访问权限
- ✅ 重写equals()时重写hashCode()
- ✅ 使用@Override注解确保正确重写
- ✅ 优先使用组合而非继承
- ✅ 设计良好的接口和抽象类
- ✅ **提供清晰的toString()**方法
- ✅ **正确实现clone()**方法
🔥 进阶学习路径
- 设计模式:学习常用的面向对象设计模式
- UML建模:掌握类图的设计和阅读
- 框架源码:阅读Spring、MyBatis等框架源码
- 性能优化:学习对象创建和内存管理
- 并发编程:掌握多线程环境下的对象设计
💡 最后建议:面向对象编程是一种思维模式,需要通过大量实践来掌握。多设计类,多写代码,多思考设计原则,逐步形成良好的编程习惯!
🎊 恭喜你完成了Java面向对象编程的学习之旅! 现在去设计你自己的类吧!