Appearance
第6课:面向对象基础
🎯 学习目标
- 理解类与对象的概念
- 掌握成员变量、成员方法、构造方法
- 理解封装的思想和实现
- 掌握 this 关键字的使用
📖 一、面向对象 vs 面向过程
C 语言(面向过程)
c
// C: 数据和函数分离
struct Dog {
char name[50];
int age;
};
void bark(struct Dog* dog) {
printf("%s is barking!\n", dog->name);
}
int main() {
struct Dog dog;
strcpy(dog.name, "旺财");
dog.age = 3;
bark(&dog); // 函数在结构体外部
}Java(面向对象)
java
// Java: 数据和方法封装在类中
class Dog {
String name;
int age;
void bark() { // 方法属于对象
System.out.println(name + " is barking!");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺财";
dog.age = 3;
dog.bark(); // 对象调用方法
}
}📖 二、类的定义
1. 基本结构
java
public class Person {
// 成员变量(属性、字段)
String name;
int age;
String gender;
// 成员方法(行为)
void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");
}
void birthday() {
age++;
System.out.println("生日快乐!现在" + age + "岁了");
}
}2. 创建对象
java
// 语法:类名 对象名 = new 类名();
Person person = new Person();
// 访问成员变量
person.name = "张三";
person.age = 25;
person.gender = "男";
// 调用成员方法
person.introduce(); // 输出:我叫张三,今年25岁
person.birthday(); // 输出:生日快乐!现在26岁了3. 内存模型
java
Person p1 = new Person();
Person p2 = new Person();
p1.name = "Alice";
p2.name = "Bob";
// p1 和 p2 是不同的对象,在堆中有独立的内存空间
System.out.println(p1.name); // Alice
System.out.println(p2.name); // Bob内存图:
栈(Stack) 堆(Heap)
┌─────────┐ ┌──────────────┐
│ p1 ───>│────────>│ Person对象 │
└─────────┘ │ name: Alice │
│ age: 0 │
┌─────────┐ └──────────────┘
│ p2 ───>│────────>┌──────────────┐
└─────────┘ │ Person对象 │
│ name: Bob │
│ age: 0 │
└──────────────┘📖 三、构造方法
1. 默认构造方法
java
public class Student {
String name;
int age;
// 如果不写构造方法,编译器自动生成无参构造
// public Student() { }
}
// 使用
Student s = new Student();2. 自定义构造方法
java
public class Student {
String name;
int age;
// 无参构造
public Student() {
System.out.println("创建了一个学生对象");
}
// 有参构造
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
// 使用
Student s1 = new Student(); // 调用无参构造
Student s2 = new Student("李四", 20); // 调用有参构造3. 构造方法重载
java
public class Book {
String title;
String author;
double price;
// 构造方法1:无参
public Book() {
this("未知书名", "未知作者", 0.0);
}
// 构造方法2:只传书名
public Book(String title) {
this(title, "未知作者", 0.0);
}
// 构造方法3:全参数
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
this.price = price;
}
}
// 使用
Book b1 = new Book();
Book b2 = new Book("Java编程思想");
Book b3 = new Book("Java编程思想", "Bruce Eckel", 99.0);⚠️ 注意:
- 构造方法名必须与类名完全相同
- 构造方法没有返回类型(连 void 都不写)
- 一旦定义了有参构造,无参构造就不会自动生成(需要手动写)
📖 四、this 关键字
1. this 代表当前对象
java
public class Person {
String name;
void setName(String name) {
// this.name 是成员变量
// name 是参数
this.name = name;
}
void introduce() {
System.out.println("我是" + this.name);
// this 可以省略
System.out.println("我是" + name);
}
}2. this 调用构造方法
java
public class Student {
String name;
int age;
String major;
public Student() {
this("未命名", 0, "未定"); // 调用三参数构造
}
public Student(String name, int age) {
this(name, age, "计算机"); // 调用三参数构造
}
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
}⚠️ 注意:
this()必须是构造方法的第一条语句- 不能循环调用(A调用B,B调用A)
3. this 的应用场景
java
public class BankAccount {
private String accountNumber;
private double balance;
// 场景1:区分成员变量和参数
public BankAccount(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
}
// 场景2:返回当前对象(链式调用)
public BankAccount deposit(double amount) {
this.balance += amount;
return this; // 返回当前对象
}
public BankAccount withdraw(double amount) {
this.balance -= amount;
return this;
}
}
// 链式调用
BankAccount account = new BankAccount("001", 1000);
account.deposit(500).withdraw(200).deposit(100);📖 五、封装
1. 为什么需要封装
java
// ❌ 不封装的问题
public class Person {
String name;
int age; // 任何人都可以随意修改
}
Person p = new Person();
p.age = -100; // 不合理的数据,但无法阻止2. 封装的实现
java
public class Person {
// private:私有,只能在本类中访问
private String name;
private int age;
// public getter:提供读取权限
public String getName() {
return name;
}
// public setter:提供修改权限,可以加入校验
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("姓名不能为空");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0 || age > 150) {
System.out.println("年龄不合理");
return;
}
this.age = age;
}
}
// 使用
Person p = new Person();
p.setAge(-100); // 输出:年龄不合理,不会设置
p.setAge(25); // 正常设置
System.out.println(p.getAge()); // 253. 访问修饰符
| 修饰符 | 本类 | 同包 | 子类 | 任何地方 |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| 默认(不写) | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
java
public class Example {
private int a; // 只能在本类访问
int b; // 默认,同包可访问
protected int c; // 同包和子类可访问
public int d; // 任何地方都可访问
}4. JavaBean 规范
java
// 标准的 JavaBean
public class User {
// 1. 成员变量私有
private String username;
private String password;
private int age;
// 2. 无参构造
public User() {
}
// 3. 有参构造(可选)
public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
}
// 4. getter/setter 方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}📖 六、对象的创建过程
java
Student s = new Student("张三", 20);
// 执行步骤:
// 1. JVM 在堆中分配内存
// 2. 成员变量初始化为默认值(0, null, false)
// 3. 执行构造方法
// 4. 返回对象的引用给 s📖 七、对象比较
1. == vs equals()
java
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
Person p3 = p1;
System.out.println(p1 == p2); // false(不同对象)
System.out.println(p1 == p3); // true(同一对象)
System.out.println(p1.equals(p2)); // 需要重写 equals 方法2. 重写 equals 方法
java
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}⚠️ 常见陷阱
陷阱1:类只是一堆 getter/setter
如果类只有字段和访问器,没有行为,往往说明领域建模还不够。对象应尽量把数据和相关行为放在一起。
陷阱2:构造器没有维护不变量
构造器应该保证对象创建后处于合法状态,不要创建“半初始化对象”。
陷阱3:直接暴露可变字段
把内部 List、数组直接返回给外部,会破坏封装。必要时返回副本或不可变视图。
陷阱4:用 == 比较对象内容
对象内容比较应使用 equals,== 比较的是引用是否相同。
🆚 Java vs C 对比
| 特性 | C struct | Java class |
|---|---|---|
| 数据与行为 | 通常分离 | 字段和方法封装在类中 |
| 构造 | 手动初始化函数 | 构造方法 |
| 封装 | 靠约定和头文件 | private/protected/public |
| 内存管理 | 手动 malloc/free | new + GC |
对 C 程序员来说,Java 类不是“带函数的 struct”这么简单。它还承担封装、生命周期初始化和行为聚合的职责。
💡 最佳实践
- 成员变量私有化,提供 getter/setter
- 构造方法中初始化对象
- 重写 toString() 方法便于调试
- 重写 equals() 和 hashCode() 方法
📝 练习预告
完成 练习/Ex06_OOP_Basic.java:
- 设计一个学生类
- 设计一个银行账户类
- 实现图书管理
- 对象数组应用
- 封装实践
- 综合:员工管理系统
🎓 下一步
- 第7课:继承 - extends、super、方法重写