Java 基础语言 ④ ------ 面向对象基础-类与对象
-
- 前言
- 一、类的定义
-
- [1.1 类的理解](#1.1 类的理解)
- [1.2 类的组成](#1.2 类的组成)
- [1.3 示例:手机类](#1.3 示例:手机类)
- [1.4 深入理解:类作为蓝图与 ADT](#1.4 深入理解:类作为蓝图与 ADT)
- 二、对象的创建与使用
-
- [2.1 创建对象:`new` 关键字](#2.1 创建对象:
new关键字) - [2.2 引用与对象的分离](#2.2 引用与对象的分离)
- [2.3 赋值即拷贝引用](#2.3 赋值即拷贝引用)
- [2.4 引用相等 vs 内容相等](#2.4 引用相等 vs 内容相等)
- [2.1 创建对象:`new` 关键字](#2.1 创建对象:
- 三、对象内存模型
-
- [3.1 内存区域回顾](#3.1 内存区域回顾)
- [3.2 单个对象内存图](#3.2 单个对象内存图)
- [3.3 多个对象内存图](#3.3 多个对象内存图)
- 四、成员变量与局部变量
-
- [4.1 四大区别](#4.1 四大区别)
- 五、构造方法
-
- [5.1 构造方法的本质:初始化而非创建](#5.1 构造方法的本质:初始化而非创建)
- [5.2 默认构造函数](#5.2 默认构造函数)
- [5.3 构造器重载](#5.3 构造器重载)
- [5.4 构造器间的互调:`this(...)`](#5.4 构造器间的互调:
this(...))
- [六、`this` 关键字](#六、
this关键字) -
- [6.1 本质:隐式的隐藏参数](#6.1 本质:隐式的隐藏参数)
- [6.2 核心应用:消除命名歧义](#6.2 核心应用:消除命名歧义)
- [6.3 `this` 的内存模型](#6.3
this的内存模型) - [6.4 关键约束](#6.4 关键约束)
- [6.5 总结](#6.5 总结)
- 总结

🎬 博主名称: 超级苦力怕
🔥 个人专栏: 《Java 后端修炼手册》《Java 基础语言》
🚀 每一次思考都是突破的前奏,每一次复盘都是精进的开始!
文章元信息:
- 标签:
#Java基础#面向对象#类与对象- 建议顺序: 04
- 前置知识: 建议先读《流程控制与数组》(③)
前言
从这一篇开始,我们正式踏入 Java 最核心的领域------面向对象编程(OOP) 。如果说流程控制和数组让程序有了"逻辑",那类与对象就让程序有了"结构"。类是蓝图,对象是实物;类是抽象的模板,对象是堆内存中真实存在的数据仓库。本文将带你从零理解类的定义、对象的创建、
new关键字背后的内存操作、构造方法的初始化职责、以及this关键字如何将对象实体与执行逻辑绑定在一起。适合刚学完基本语法、准备系统入门面向对象的 Java 初学者,也适合想搞懂底层内存模型的进阶读者。
一、类的定义
1.1 类的理解
客观存在的事物皆为对象,所以我们也常说万物皆对象。
类是对现实生活中一类具有共同属性和行为的事物的抽象。它不是具体实体,而是一张"蓝图"------规定了这一类事物有哪些特征(属性)以及能做什么(行为)。
| 概念 | 理解 | 举例 |
|---|---|---|
| 类(Class) | 对一类事物的抽象描述,是对象的数据类型 和模板 | "手机"这个类别 |
| 对象(Object) | 看得见摸得着的具体实体 | 你口袋里那台 iPhone 16 |
简单理解:类是对事物的一种描述,对象是具体存在的事物。类是"图纸",对象是按图纸造出来的"实物"。

1.2 类的组成
类由两部分组成:
-
属性(字段/成员变量):描述事物的特征,在类中方法外定义
-
行为(成员方法) :描述事物能执行的操作,与普通方法的区别是去掉了
static关键字类的组成:
├── 属性 → 成员变量(Field / Instance Variable)
│ 例如:手机有品牌(brand)、价格(price)
└── 行为 → 成员方法(Method)
例如:手机可以打电话(call)、发短信(sendMessage)
类的定义步骤:
- 定义类:
public class 类名 { } - 编写成员变量:
数据类型 变量名; - 编写成员方法:去掉
static关键字
java
public class 类名 {
// 成员变量
数据类型 变量1;
数据类型 变量2;
// 成员方法
public 返回类型 方法名() {
// 方法体
}
}
1.3 示例:手机类
java
/*
手机类 Phone
成员变量:
品牌 brand (String)
价格 price (int)
成员方法:
打电话 call()
发短信 sendMessage()
*/
public class Phone {
// 成员变量
String brand;
int price;
// 成员方法
public void call() {
System.out.println("打电话");
}
public void sendMessage() {
System.out.println("发短信");
}
}
⚠️ 注意 :成员变量声明在类中、方法外。它们有默认初始值(
int为0,引用类型为null),不需要手动初始化即可使用。
1.4 深入理解:类作为蓝图与 ADT
从软件工程的视角看,类不仅是代码的"收纳盒",更承担了两个关键角色:
① 类定义了对象的内存布局
类规定了该类型的每个对象在堆内存中需要分配多少空间、存储哪些变量。编译器根据类定义,精确计算每个对象所需的内存大小。
② 类是实现抽象数据类型(ADT)的工具
通过将字段设为 private 并仅暴露 public 方法,类可以强制维护不变性------即内部数据始终处于合法状态。外部代码无法直接"弄脏"对象内部的数据结构。
java
public class Date {
private int day;
private int month;
// 通过构造方法强制校验
public Date(int day, int month) {
if (day < 1 || day > 31 || month < 1 || month > 12) {
throw new IllegalArgumentException("日期不合法");
}
this.day = day;
this.month = month;
}
}
这种"隐藏内部实现、只暴露安全接口"的思想,贯穿了 Java 的整个面向对象设计。
二、对象的创建与使用
2.1 创建对象:new 关键字
对象是通过 new 关键字创建的。new 在堆内存中为对象分配空间,然后调用构造方法初始化对象的成员变量。
格式:
java
类名 对象名 = new 类名();
这条语句拆开看,其实做了三件事:
Phone p = new Phone();
│ │ │
│ │ └── ③ 调用构造方法,初始化对象
│ └── ② 在堆内存中为对象分配空间
└── ① 在栈内存中声明一个引用变量 p
![[new关键字执行三步.png]]
✅ 创建对象并访问成员
java
public class PhoneDemo {
public static void main(String[] args) {
// 创建对象
Phone p = new Phone();
// 访问成员变量(默认值)
System.out.println(p.brand); // null
System.out.println(p.price); // 0
// 为成员变量赋值
p.brand = "小米";
p.price = 2999;
System.out.println(p.brand); // 小米
System.out.println(p.price); // 2999
// 调用成员方法
p.call(); // 打电话
p.sendMessage(); // 发短信
}
}
⚠️ 区分字段与方法 :
p.brand没有括号,访问的是字段 ;p.call()带有括号,调用的是方法。
2.2 引用与对象的分离
这是 Java 中最容易混淆的概念之一,必须明确区分

- 引用变量 存储在栈(或包含它的对象)中,本质是一个内存地址
- 对象实体始终在堆中,包含实际的成员变量数据
Phone p = new Phone();中,p是引用变量,new Phone()才是对象
2.3 赋值即拷贝引用
当一个引用变量赋值给另一个引用变量时,拷贝的是地址,不是对象本身。
java
Phone p1 = new Phone();
p1.brand = "华为";
Phone p2 = p1; // p2 和 p1 指向堆中**同一个**Phone 对象
p2.brand = "苹果"; // 通过 p2 修改,实际上改的是同一个对象
System.out.println(p1.brand); // "苹果" ← p1 看到的也变了!
赋值前: 赋值后:
p1 ──▶ [Phone对象] p1 ──▶ [Phone对象]
↗
p2
关键结论:两个引用指向同一个对象,通过任意一个引用修改数据,另一个引用也会"看到"这个变化。
2.4 引用相等 vs 内容相等
java
Phone a = new Phone();
a.brand = "三星";
Phone b = new Phone();
b.brand = "三星";
System.out.println(a == b); // false ← 比较的是地址
System.out.println(a.brand.equals(b.brand)); // true ← 比较的是内容
| 比较方式 | 比较什么 | 适用场景 |
|---|---|---|
== |
两个引用是否指向同一个对象(地址相等) | 判断是否同一实例 |
.equals() |
两个对象是否内容相同(逻辑相等) | 判断数据是否等价 |
默认情况下
.equals()等同于==。需要内容比较时,必须在类中重写equals()方法,后面的继承章节会详细讨论。
三、对象内存模型
3.1 内存区域回顾
JVM 将内存划分为三个主要区域:
| 内存区域 | 存储内容 | 生命周期 | 共享性 |
|---|---|---|---|
| 栈(Stack) | 局部变量、方法参数、引用变量 | 方法执行期间 | 线程私有 |
| 堆(Heap) | 对象实体(成员变量)、数组 | 由 GC 管理 | 线程共享 |
| 方法区(Method Area) | 类的字节码、静态变量、方法代码 | 类加载到卸载 | 线程共享 |

3.2 单个对象内存图
栈内存 堆内存 方法区
┌──────────────┐ ┌──────────────┐ ┌─────────────────┐
│ main() │ │ Phone对象 │ │ Phone.class │
│ │ │ │ │ │
│ Phone p ────┼──────────────┼──▶ brand=null│ │ void call() │
│ │ │ price=0 │◀────────│ void sendMsg() │
└──────────────┘ └──────────────┘ └─────────────────┘
执行 p.brand = "小米"; p.price = 2999; 后:
栈内存 堆内存
┌──────────────┐ ┌──────────────┐
│ main() │ │ Phone对象 │
│ │ │ │
│ Phone p ────┼──────────────┼──▶ brand="小米"│
│ │ │ price=2999 │
└──────────────┘ └──────────────┘
调用 p.call() 时,JVM 找到 p 指向的对象,拿到对象的类信息,在方法区中找到 call() 的字节码并执行。方法执行时,会在栈顶新开一个栈帧。

3.3 多个对象内存图
java
Phone p1 = new Phone();
p1.brand = "华为";
Phone p2 = new Phone();
p2.brand = "苹果";

结论:
- 成员变量 :每个对象在堆中有独立的内存区域,各自存储各自的变量值
- 成员方法 :多个对象共享方法区中的同一份代码 ------方法代码只存一份,通过
this区分是哪个对象在调用
四、成员变量与局部变量
4.1 四大区别
| 区别维度 | 成员变量 | 局部变量 |
|---|---|---|
| 类中位置 | 类中、方法外 | 方法内部或方法声明(参数)上 |
| 内存中位置 | 堆内存 | 栈内存 |
| 生命周期 | 随对象存在而存在,随对象被 GC 回收而消失 | 随方法调用而存在,方法结束即消失 |
| 初始化值 | 有默认值(int→0, double→0.0, 引用→null, boolean→false) |
无默认值,必须先赋值后使用 |
✅ 位置差异示例
java
public class Student {
// 成员变量 ------ 类中方法外
String name;
int age;
public void study() {
int hours = 2; // 局部变量 ------ 方法内部
System.out.println(name + "学习了" + hours + "小时");
}
public void exam(int score) { // score ------ 局部变量(方法参数)
System.out.println(name + "考了" + score + "分");
}
}
⚠️ 如果在方法内部定义了与成员变量同名的局部变量,局部变量会遮蔽成员变量 。此时必须用
this.变量名来访问成员变量。这引出了下一节的内容------this关键字。

五、构造方法
5.1 构造方法的本质:初始化而非创建
构造方法常被误解为"创建对象的方法",但事实上:
- JVM 负责创建对象------在堆中分配空间、赋予默认值
- 构造方法负责初始化------把有意义的值填入已分配好的空间
语法特征:
- 方法名必须与类名完全相同
- 没有返回类型 (连
void都不能写) - 通过
new关键字调用
java
public class Student {
String name;
int age;
// 构造方法
public Student(String n, int a) {
name = n; // 初始化 name 字段
age = a; // 初始化 age 字段
}
}
new Student("张三", 18) 的执行过程:
① JVM 在堆中分配空间,字段获得默认值(name=null, age=0)
↓
② JVM 调用构造方法 Student("张三", 18)
↓
③ 构造方法将 name 设为 "张三",age 设为 18
↓
④ 返回对象的引用地址

5.2 默认构造函数
如果类中没有写任何 构造方法,Java 会自动提供一个默认的无参构造方法:
java
public class Student {
String name;
int age;
// 没有显式写构造方法
// Java 自动提供:public Student() { }
}
// 使用时:
Student s = new Student(); // 可以,有默认无参构造
⚠️ 关键规则 :一旦你手动写了任意一个 构造方法(无论是否有参),Java 就不再自动提供默认无参构造。
java
public class Student {
String name;
int age;
// 自定义了一个有参构造
public Student(String n, int a) {
name = n;
age = a;
}
}
Student s1 = new Student("张三", 18); // ✅ 可以
Student s2 = new Student(); // ❌ 编译错误!无参构造已消失
解决方案:如果仍然需要无参构造,必须手动写出:
java
public Student() { } // 手动添加无参构造
public Student(String n, int a) { ... } // 有参构造
5.3 构造器重载
一个类可以有多个构造方法,只要它们的参数数量或类型不同。(这和方法重载的规则一样。)
java
public class Phone {
String brand;
int price;
// 无参构造
public Phone() { }
// 只传品牌
public Phone(String b) {
brand = b;
}
// 传品牌和价格
public Phone(String b, int p) {
brand = b;
price = p;
}
}
// 三种创建方式
Phone p1 = new Phone(); // 无参
Phone p2 = new Phone("华为"); // 一个参数
Phone p3 = new Phone("苹果", 5999); // 两个参数
5.4 构造器间的互调:this(...)
当一个类有多个构造方法时,可以用 this(...) 让一个构造方法调用另一个,避免重复代码。
java
public class Phone {
String brand;
int price;
// 主构造方法
public Phone(String brand, int price) {
this.brand = brand;
this.price = price;
}
// 只传品牌------委托给主构造方法
public Phone(String brand) {
this(brand, 0); // 调用 Phone(String, int),价格默认为 0
}
// 无参------委托给单参构造方法
public Phone() {
this("未知品牌"); // 调用 Phone(String)
}
}
⚠️
this(...)必须是构造方法体中的第一条语句 ,且一个构造方法中只能调用一次this(...)。

六、this 关键字
6.1 本质:隐式的隐藏参数
this 是指向当前正在操作的对象 的引用。每当调用非静态方法时,Java 会自动将调用该方法的对象的引用作为隐藏参数 传入,这个隐藏参数就是 this。
java
public class Student {
String name;
public void introduce() {
// 这里的 this 就是调用 introduce 的那个对象
System.out.println("我是" + this.name);
}
}
Student s1 = new Student();
s1.name = "张三";
s1.introduce(); // 隐藏传入了 s1 → method 内部 this = s1
Student s2 = new Student();
s2.name = "李四";
s2.introduce(); // 隐藏传入了 s2 → method 内部 this = s2
6.2 核心应用:消除命名歧义
当方法的参数名与成员变量名相同时,局部变量(参数)会遮蔽成员变量 。此时必须用 this.变量名 来访问成员变量:
java
public class Student {
private String name; // 成员变量
private int age; // 成员变量
// 参数名故意与字段名相同------这是常见的命名惯例
public Student(String name, int age) {
this.name = name; // this.name 是成员变量,name 是参数
this.age = age; // this.age 是成员变量,age 是参数
}
public void setName(String name) {
this.name = name; // 同样:this.name 是字段,name 是参数
}
}
在构造方法和 Setter 中,
this.字段 = 参数是最常见的 Java 编码惯例。
6.3 this 的内存模型
this 本身是一个局部变量,存储在方法调用的栈帧中:

6.4 关键约束
① 静态方法中没有 this
静态方法通过 static 修饰,属于类本身而非对象,调用时不会传入对象引用,因此内部不存在 this。
java
public class Student {
String name;
public static void printInfo() {
System.out.println(this.name); // ❌ 编译错误!静态方法中不能使用 this
}
}
② this 不可被重新赋值
在 Java 中,this 是一个不可改变的隐式 final 参数 。你不能让 this 指向另一个对象。
java
public void someMethod() {
this = new Student(); // ❌ 编译错误!不能给 this 赋值
}
6.5 总结
this 的用法 |
说明 | 场景 |
|---|---|---|
this.字段名 |
访问当前对象的成员变量 | 参数与字段同名时消除歧义 |
this.方法名() |
调用当前对象的另一个成员方法 | 方法间调用(通常可省略 this) |
this(...) |
调用本类的另一个构造方法 | 构造器重载中消除重复代码 |
this是 Java 中连接"对象实体"与"执行逻辑"的纽带------它确保了方法始终知道自己操作的是哪个对象的数据。

总结
| 知识点 | 核心要点 |
|---|---|
| 类与对象 | 类是蓝图/模板,对象是堆内存中的具体实体;类 = 成员变量(属性)+ 成员方法(行为) |
new 关键字 |
三步:声明引用变量(栈)→ 分配堆空间 → 调用构造方法初始化 |
| 引用与对象 | 引用变量存地址,对象实体在堆中;赋值拷贝的是引用,不是对象本身 |
== vs equals() |
== 比较地址(同一对象),equals() 比较内容(需重写) |
| 对象内存模型 | 栈存局部变量/引用,堆存对象实体,方法区存类信息/方法字节码;成员变量各对象独立,成员方法多对象共享 |
| 成员变量 vs 局部变量 | 位置不同(类中方法外 vs 方法内)、内存不同(堆 vs 栈)、生命周期不同、默认值不同 |
| 构造方法 | 只初始化不创建;与类同名无返回类型;一旦自定义构造方法,默认无参构造消失;支持重载;this(...) 实现构造器链式调用 |
this 关键字 |
隐式传入当前对象的引用;消除参数与字段的命名歧义;静态方法中不存在;不可被重新赋值 |
💡 核心结论: 类定义了对象的"长相"和"行为",new 在堆中把蓝图变成实物,构造方法为实物填充初始数据,this 确保每个方法都知道自己在操作哪个对象。理解这四者的关系,就理解了 Java 面向对象编程的基石。
💡 关键提醒: 始终牢记引用与对象的分离------一个引用变量只是一个"遥控器",多个遥控器可以指向同一个对象;构造方法的第一职责是初始化而非创建;在构造方法和 Setter 中坚持使用 this.字段 = 参数 的命名惯例,可以有效避免字段遮蔽带来的隐蔽 Bug。
