Java 面向对象
Java 面向对象:从思想到类与对象,真正迈入 OOP 的大门
学习日期:2026-06-03 难度:⭐⭐⭐ 进阶入门
一、为什么需要面向对象?
先回顾一下我们之前学的:流程控制、数组——这些都是面向过程的写法:关注"第一步做什么、第二步做什么"。
但现实世界不是这样的。你点外卖,不会去想"餐厅怎么炒菜、骑手怎么导航",你只关心有什么对象、对象能做什么:
plaintext
9
1
2
3
面向过程:炒菜 → 打包 → 取餐 → 骑行 → 送达
面向对象:餐厅.做餐() → 骑手.配送() → 我.收餐()
面向过程像写菜谱,一步一步来;面向对象像导演安排演员,每个角色各司其职。
表格
| 对比 | 面向过程 | 面向对象 |
|---|---|---|
| 核心思想 | 以步骤为中心 | 以对象为中心 |
| 适合场景 | 简单脚本、小工具 | 大型系统、复杂业务 |
| 复用方式 | 函数调用 | 继承、组合 |
| 扩展性 | 改动影响大 | 改动局部化 |
二、面向对象三大特征(先有个概念)
这仨是 OOP 的灵魂,后面会逐个深入,今天先记住名字:
- 封装:把数据藏起来,只暴露必要的操作 → 安全、可控
- 继承:子类复用父类的代码 → 避免重复
- 多态:同一个方法,不同对象有不同的表现 → 灵活、可扩展
🎯 今天重点搞懂"类与对象",封装会顺带涉及,继承和多态后面专门学。
三、类与对象:图纸和房子的关系
3.1 一句话区分
- 类(Class) :是设计图纸,描述一类事物有什么、能做什么
- 对象(Object) :是根据图纸造出来的具体实例
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 类 = 图纸
class Phone {
String brand; // 有什么:品牌
double price; // 有什么:价格
void call() { // 能做什么:打电话
System.out.println("用" + brand + "打电话");
}
}
// 对象 = 根据图纸造出来的真手机
Phone myPhone = new Phone();
myPhone.brand = "小米";
myPhone.call(); // 用小米打电话
plaintext
9
1
2
3
4
5
6
7
类(Phone) 对象1 对象2
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ brand │ │ brand = "小米"│ │ brand = "苹果"│
│ price │ ──→ │ price = 2999 │ │ price = 8999 │
│ call() │ │ call() │ │ call() │
└──────────────┘ └──────────────┘ └──────────────┘
💡 一个类可以创建无数个对象,每个对象独立拥有自己的数据。
3.2 类的定义语法
java
99
1
2
3
4
5
6
7
8
9
10
class 类名 {
// 1. 成员变量(属性)—— 描述"有什么"
数据类型 变量名;
// 2. 成员方法(行为)—— 描述"能做什么"
返回值类型 方法名(参数列表) {
// 方法体
}
}
来个完整的例子:
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student {
// 属性
String name;
int age;
String major;
// 行为
void study() {
System.out.println(name + "正在学习" + major);
}
void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");
}
}
3.3 创建和使用对象
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
// 创建对象:new 类名()
Student stu1 = new Student();
// 给属性赋值
stu1.name = "张三";
stu1.age = 20;
stu1.major = "计算机科学";
// 调用方法
stu1.introduce(); // 我叫张三,今年20岁
stu1.study(); // 张三正在学习计算机科学
// 再创建一个,互不影响
Student stu2 = new Student();
stu2.name = "李四";
stu2.age = 22;
stu2.introduce(); // 我叫李四,今年22岁
}
}
四、构造方法:对象的"出生证明"
4.1 什么是构造方法?
每次 new Student() 时调用的那个特殊方法,就是构造方法。它负责初始化对象。
特点:
- 方法名和类名完全一致
- 没有返回值类型(连 void 都不写)
- 创建对象时自动调用,不能手动调
4.2 默认构造方法
如果你不写任何构造方法,Java 会偷偷给你加一个无参构造:
java
9
1
2
3
4
5
6
7
class Student {
String name;
int age;
// 你没写,但 Java 默认存在:
// Student() { }
}
4.3 自定义构造方法
一旦你自己写了构造方法,默认的就没了!
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student {
String name;
int age;
// 无参构造
Student() {
System.out.println("一个学生诞生了!");
}
// 有参构造:创建时直接赋值
Student(String n, int a) {
name = n;
age = a;
}
}
// 使用
Student stu1 = new Student(); // 调用无参构造
Student stu2 = new Student("王五", 21); // 调用有参构造
⚠️ 常见坑:如果你只写了有参构造,没写无参构造,那
new Student()会报错!建议:写了有参构造时,顺手补一个无参构造。
五、this 关键字:指代"我自己"
5.1 解决命名冲突
当成员变量和局部变量同名时,用 this 区分:
java
99
1
2
3
4
5
6
7
8
9
10
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name; // this.name = 成员变量,name = 参数
this.age = age;
}
}
💡 这是最常见用法,IDE 生成构造方法时自动带 this。
5.2 在构造方法中调用另一个构造方法
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student {
String name;
int age;
String major;
Student(String name, int age) {
this.name = name;
this.age = age;
}
Student(String name, int age, String major) {
this(name, age); // 调用上面的两参构造
this.major = major; // 再额外设置 major
}
}
⚠️
this()必须放在构造方法的第一行,否则编译报错。
六、封装:别让人随便动我的数据
6.1 问题在哪?
java
9
1
2
3
Student stu = new Student();
stu.age = -999; // 年龄设成负数?不合理但编译不报错!
成员变量直接暴露,任何人都能乱赋值——这不安全。
6.2 解决方案:private + getter/setter
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Student {
private String name;
private int age; // 私有:外部不能直接访问
// getter:读取
public int getAge() {
return age;
}
// setter:写入(加校验)
public void setAge(int age) {
if (age < 0 || age > 150) {
System.out.println("年龄不合法!");
return;
}
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用:
java
9
1
2
3
4
5
Student stu = new Student();
stu.setAge(-999); // 年龄不合法!赋值失败
stu.setAge(20); // ✅ 合法
System.out.println(stu.getAge()); // 20
封装的原则:
- 属性用
private修饰 - 对外提供
public的 getter/setter - 在 setter 中做数据校验
表格
| 访问修饰符 | 同类 | 同包 | 不同包 |
|---|---|---|---|
| private | ✅ | ❌ | ❌ |
| 默认(不写) | ✅ | ✅ | ❌ |
| protected | ✅ | ✅ | ✅(子类) |
| public | ✅ | ✅ | ✅ |
七、成员变量 vs 局部变量
表格
| 对比 | 成员变量 | 局部变量 |
|---|---|---|
| 位置 | 类中,方法外 | 方法内 |
| 默认值 | 有(0、null、false等) | 无,必须先赋值才能用 |
| 作用域 | 整个类 | 从定义到方法结束 |
| 生命周期 | 对象创建到销毁 | 方法调用到结束 |
java
99
1
2
3
4
5
6
7
8
9
10
11
12
class Demo {
int a; // 成员变量,默认值0
void test() {
int b; // 局部变量,无默认值
// System.out.println(b); // ❌ 编译报错:可能未初始化
b = 10;
System.out.println(b); // ✅ 10
System.out.println(a); // ✅ 0(成员变量有默认值)
}
}
八、方法重载(Overload)
同一个类中,方法名相同、参数列表不同——这就是重载。
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) { // 参数类型不同
return a + b;
}
int add(int a, int b, int c) { // 参数个数不同
return a + b + c;
}
}
重载规则:
- ✅ 参数个数不同
- ✅ 参数类型不同
- ✅ 参数顺序不同
- ❌ 仅返回值不同——不算重载,编译报错
💡 构造方法也可以重载,前面的
Student()和Student(String, int)就是。
九、static:属于类,不属于对象
9.1 静态变量——所有对象共享
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student {
String name;
static String school = "清华大学"; // 静态变量:所有学生共享
Student(String name) {
this.name = name;
}
}
Student stu1 = new Student("张三");
Student stu2 = new Student("李四");
// 通过类名访问(推荐)
System.out.println(Student.school); // 清华大学
// 修改后所有人都能看到
Student.school = "北京大学";
System.out.println(stu1.school); // 北京大学
System.out.println(stu2.school); // 北京大学
9.2 静态方法——不依赖对象就能调用
java
9
1
2
3
4
5
6
7
8
9
class MathUtils {
static int max(int a, int b) {
return a > b ? a : b;
}
}
// 不需要 new 对象,直接用类名调用
int result = MathUtils.max(10, 20); // 20
⚠️ 静态方法不能直接访问非静态成员(因为没有对象实例):
java
9
1
2
3
4
5
6
7
class Demo {
int a = 10;
static void test() {
// System.out.println(a); // ❌ 编译报错!
}
}
9.3 什么时候用 static?
表格
| 场景 | 选择 |
|---|---|
| 每个对象各自拥有的数据 | 普通成员变量 |
| 所有对象共享的数据 | static 变量 |
| 需要访问对象属性的方法 | 普通成员方法 |
工具类方法(不依赖对象状态) | static 方法 |
十、代码块:初始化的另一种方式
10.1 构造代码块
每次创建对象时都会执行,在构造方法之前运行:
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Student {
String name;
// 构造代码块
{
System.out.println("一个新的学生正在注册...");
}
Student() {
System.out.println("无参构造执行");
}
Student(String name) {
this.name = name;
System.out.println("有参构造执行");
}
}
// 无论调哪个构造方法,都会先执行构造代码块
new Student(); // 新的学生正在注册... → 无参构造执行
new Student("张三"); // 新的学生正在注册... → 有参构造执行
10.2 静态代码块
类加载时执行一次,且只执行一次:
java
9
1
2
3
4
5
6
7
8
9
class Config {
static String version;
static {
version = "v1.0";
System.out.println("静态代码块:类加载时执行一次");
}
}
执行顺序:静态代码块 → 构造代码块 → 构造方法
十一、实战练习
练习1:设计一个 BankAccount 类
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BankAccount {
private String owner;
private double balance;
BankAccount(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
void deposit(double amount) {
if (amount <= 0) {
System.out.println("存款金额必须大于0");
return;
}
balance += amount;
System.out.println(owner + "存入" + amount + "元,余额:" + balance);
}
void withdraw(double amount) {
if (amount > balance) {
System.out.println("余额不足!");
return;
}
balance -= amount;
System.out.println(owner + "取出" + amount + "元,余额:" + balance);
}
void showBalance() {
System.out.println(owner + "的账户余额:" + balance + "元");
}
}
练习2:统计创建了多少个对象
java
99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User {
String name;
static int count = 0; // 静态变量:计数器
User(String name) {
this.name = name;
count++; // 每创建一个对象,计数+1
System.out.println("创建用户:" + name + ",当前总人数:" + count);
}
}
new User("张三"); // 创建用户:张三,当前总人数:1
new User("李四"); // 创建用户:李四,当前总人数:2
new User("王五"); // 创建用户:王五,当前总人数:3
十二、总结速查表
表格
| 概念 | 要点 |
|---|---|
| 类 vs 对象 | 类是图纸,对象是实例;一个类可以创建多个对象 |
| 构造方法 | 与类同名、无返回值、new 时自动调用;不写则有默认无参构造 |
| this | 指代当前对象;解决变量同名冲突;this() 调用其他构造方法 |
| 封装 | private 属性 + public getter/setter + 数据校验 |
| 成员变量 vs 局部变量 | 成员变量有默认值、属于对象;局部变量无默认值、属于方法 |
| 方法重载 | 同名不同参(个数/类型/顺序),与返回值无关 |
| static | 属于类而非对象;静态方法不能直接访问非静态成员 |
| 代码块 | 静态代码块(类加载时一次)→ 构造代码块(每次new)→ 构造方法 |
面向对象是 Java 的灵魂,今天搞懂了类和对象,后面学继承和多态就有了根基。你已经站在了 OOP 的大门口,推门进去吧!💪
更多推荐
所有评论(0)