news 2026/5/14 3:54:34

Java基础语言 ④ :面向对象核心——构造方法、this关键字与对象内存模型详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java基础语言 ④ :面向对象核心——构造方法、this关键字与对象内存模型详解

Java 基础语言 ④ —— 面向对象基础-类与对象

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

类的定义步骤:

  1. 定义类:public class 类名 { }
  2. 编写成员变量:数据类型 变量名;
  3. 编写成员方法:去掉static关键字
publicclass类名{// 成员变量数据类型 变量1;数据类型 变量2;// 成员方法public返回类型 方法名(){// 方法体}}

1.3 示例:手机类

/* 手机类 Phone 成员变量: 品牌 brand (String) 价格 price (int) 成员方法: 打电话 call() 发短信 sendMessage() */publicclassPhone{// 成员变量Stringbrand;intprice;// 成员方法publicvoidcall(){System.out.println("打电话");}publicvoidsendMessage(){System.out.println("发短信");}}

⚠️注意:成员变量声明在类中、方法外。它们有默认初始值(int0,引用类型为null),不需要手动初始化即可使用。

1.4 深入理解:类作为蓝图与 ADT

从软件工程的视角看,类不仅是代码的"收纳盒",更承担了两个关键角色:

① 类定义了对象的内存布局

类规定了该类型的每个对象在堆内存中需要分配多少空间、存储哪些变量。编译器根据类定义,精确计算每个对象所需的内存大小。

② 类是实现抽象数据类型(ADT)的工具

通过将字段设为private并仅暴露public方法,类可以强制维护不变性——即内部数据始终处于合法状态。外部代码无法直接"弄脏"对象内部的数据结构。

publicclassDate{privateintday;privateintmonth;// 通过构造方法强制校验publicDate(intday,intmonth){if(day<1||day>31||month<1||month>12){thrownewIllegalArgumentException("日期不合法");}this.day=day;this.month=month;}}

这种"隐藏内部实现、只暴露安全接口"的思想,贯穿了 Java 的整个面向对象设计。


二、对象的创建与使用

2.1 创建对象:new关键字

对象是通过new关键字创建的。new在堆内存中为对象分配空间,然后调用构造方法初始化对象的成员变量。

格式:

类名 对象名=new类名();

这条语句拆开看,其实做了三件事:

Phone p = new Phone(); │ │ │ │ │ └── ③ 调用构造方法,初始化对象 │ └── ② 在堆内存中为对象分配空间 └── ① 在栈内存中声明一个引用变量 p

![[new关键字执行三步.png]]
✅ 创建对象并访问成员

publicclassPhoneDemo{publicstaticvoidmain(String[]args){// 创建对象Phonep=newPhone();// 访问成员变量(默认值)System.out.println(p.brand);// nullSystem.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 赋值即拷贝引用

当一个引用变量赋值给另一个引用变量时,拷贝的是地址,不是对象本身

Phonep1=newPhone();p1.brand="华为";Phonep2=p1;// p2 和 p1 指向堆中**同一个**Phone 对象p2.brand="苹果";// 通过 p2 修改,实际上改的是同一个对象System.out.println(p1.brand);// "苹果" ← p1 看到的也变了!
赋值前: 赋值后: p1 ──▶ [Phone对象] p1 ──▶ [Phone对象] ↗ p2

关键结论:两个引用指向同一个对象,通过任意一个引用修改数据,另一个引用也会"看到"这个变化。

2.4 引用相等 vs 内容相等

Phonea=newPhone();a.brand="三星";Phoneb=newPhone();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 多个对象内存图

Phonep1=newPhone();p1.brand="华为";Phonep2=newPhone();p2.brand="苹果";

结论

  • 成员变量:每个对象在堆中有独立的内存区域,各自存储各自的变量值
  • 成员方法:多个对象共享方法区中的同一份代码——方法代码只存一份,通过this区分是哪个对象在调用

四、成员变量与局部变量

4.1 四大区别

区别维度成员变量局部变量
类中位置类中、方法外方法内部或方法声明(参数)上
内存中位置堆内存栈内存
生命周期随对象存在而存在,随对象被 GC 回收而消失随方法调用而存在,方法结束即消失
初始化值有默认值(int→0,double→0.0, 引用→null,booleanfalse无默认值,必须先赋值后使用

✅ 位置差异示例

publicclassStudent{// 成员变量 —— 类中方法外Stringname;intage;publicvoidstudy(){inthours=2;// 局部变量 —— 方法内部System.out.println(name+"学习了"+hours+"小时");}publicvoidexam(intscore){// score —— 局部变量(方法参数)System.out.println(name+"考了"+score+"分");}}

⚠️ 如果在方法内部定义了与成员变量同名的局部变量,局部变量会遮蔽成员变量。此时必须用this.变量名来访问成员变量。这引出了下一节的内容——this关键字。


五、构造方法

5.1 构造方法的本质:初始化而非创建

构造方法常被误解为"创建对象的方法",但事实上:

  • JVM 负责创建对象——在堆中分配空间、赋予默认值
  • 构造方法负责初始化——把有意义的值填入已分配好的空间

语法特征:

  • 方法名必须与类名完全相同
  • 没有返回类型(连void都不能写)
  • 通过new关键字调用
publicclassStudent{Stringname;intage;// 构造方法publicStudent(Stringn,inta){name=n;// 初始化 name 字段age=a;// 初始化 age 字段}}
new Student("张三", 18) 的执行过程: ① JVM 在堆中分配空间,字段获得默认值(name=null, age=0) ↓ ② JVM 调用构造方法 Student("张三", 18) ↓ ③ 构造方法将 name 设为 "张三",age 设为 18 ↓ ④ 返回对象的引用地址

5.2 默认构造函数

如果类中没有写任何构造方法,Java 会自动提供一个默认的无参构造方法

publicclassStudent{Stringname;intage;// 没有显式写构造方法// Java 自动提供:public Student() { }}// 使用时:Students=newStudent();// 可以,有默认无参构造

⚠️关键规则:一旦你手动写了任意一个构造方法(无论是否有参),Java 就不再自动提供默认无参构造。

publicclassStudent{Stringname;intage;// 自定义了一个有参构造publicStudent(Stringn,inta){name=n;age=a;}}Students1=newStudent("张三",18);// ✅ 可以Students2=newStudent();// ❌ 编译错误!无参构造已消失

解决方案:如果仍然需要无参构造,必须手动写出:

publicStudent(){}// 手动添加无参构造publicStudent(Stringn,inta){...}// 有参构造

5.3 构造器重载

一个类可以有多个构造方法,只要它们的参数数量或类型不同。(这和方法重载的规则一样。)

publicclassPhone{Stringbrand;intprice;// 无参构造publicPhone(){}// 只传品牌publicPhone(Stringb){brand=b;}// 传品牌和价格publicPhone(Stringb,intp){brand=b;price=p;}}// 三种创建方式Phonep1=newPhone();// 无参Phonep2=newPhone("华为");// 一个参数Phonep3=newPhone("苹果",5999);// 两个参数

5.4 构造器间的互调:this(...)

当一个类有多个构造方法时,可以用this(...)让一个构造方法调用另一个,避免重复代码。

publicclassPhone{Stringbrand;intprice;// 主构造方法publicPhone(Stringbrand,intprice){this.brand=brand;this.price=price;}// 只传品牌——委托给主构造方法publicPhone(Stringbrand){this(brand,0);// 调用 Phone(String, int),价格默认为 0}// 无参——委托给单参构造方法publicPhone(){this("未知品牌");// 调用 Phone(String)}}

⚠️this(...)必须是构造方法体中的第一条语句,且一个构造方法中只能调用一次this(...)


六、this关键字

6.1 本质:隐式的隐藏参数

this是指向当前正在操作的对象的引用。每当调用非静态方法时,Java 会自动将调用该方法的对象的引用作为隐藏参数传入,这个隐藏参数就是this

publicclassStudent{Stringname;publicvoidintroduce(){// 这里的 this 就是调用 introduce 的那个对象System.out.println("我是"+this.name);}}Students1=newStudent();s1.name="张三";s1.introduce();// 隐藏传入了 s1 → method 内部 this = s1Students2=newStudent();s2.name="李四";s2.introduce();// 隐藏传入了 s2 → method 内部 this = s2

6.2 核心应用:消除命名歧义

当方法的参数名与成员变量名相同时,局部变量(参数)会遮蔽成员变量。此时必须用this.变量名来访问成员变量:

publicclassStudent{privateStringname;// 成员变量privateintage;// 成员变量// 参数名故意与字段名相同——这是常见的命名惯例publicStudent(Stringname,intage){this.name=name;// this.name 是成员变量,name 是参数this.age=age;// this.age 是成员变量,age 是参数}publicvoidsetName(Stringname){this.name=name;// 同样:this.name 是字段,name 是参数}}

在构造方法和 Setter 中,this.字段 = 参数是最常见的 Java 编码惯例。

6.3this的内存模型

this本身是一个局部变量,存储在方法调用的栈帧中:

6.4 关键约束

① 静态方法中没有this

静态方法通过static修饰,属于类本身而非对象,调用时不会传入对象引用,因此内部不存在this

publicclassStudent{Stringname;publicstaticvoidprintInfo(){System.out.println(this.name);// ❌ 编译错误!静态方法中不能使用 this}}

this不可被重新赋值

在 Java 中,this是一个不可改变的隐式 final 参数。你不能让this指向另一个对象。

publicvoidsomeMethod(){this=newStudent();// ❌ 编译错误!不能给 this 赋值}

6.5 总结

this的用法说明场景
this.字段名访问当前对象的成员变量参数与字段同名时消除歧义
this.方法名()调用当前对象的另一个成员方法方法间调用(通常可省略this
this(...)调用本类的另一个构造方法构造器重载中消除重复代码

this是 Java 中连接"对象实体"与"执行逻辑"的纽带——它确保了方法始终知道自己操作的是哪个对象的数据。


总结

知识点核心要点
类与对象类是蓝图/模板,对象是堆内存中的具体实体;类 = 成员变量(属性)+ 成员方法(行为)
new关键字三步:声明引用变量(栈)→ 分配堆空间 → 调用构造方法初始化
引用与对象引用变量存地址,对象实体在堆中;赋值拷贝的是引用,不是对象本身
==vsequals()==比较地址(同一对象),equals()比较内容(需重写)
对象内存模型栈存局部变量/引用,堆存对象实体,方法区存类信息/方法字节码;成员变量各对象独立,成员方法多对象共享
成员变量 vs 局部变量位置不同(类中方法外 vs 方法内)、内存不同(堆 vs 栈)、生命周期不同、默认值不同
构造方法只初始化不创建;与类同名无返回类型;一旦自定义构造方法,默认无参构造消失;支持重载;this(...)实现构造器链式调用
this关键字隐式传入当前对象的引用;消除参数与字段的命名歧义;静态方法中不存在;不可被重新赋值

💡 核心结论:类定义了对象的"长相"和"行为",new在堆中把蓝图变成实物,构造方法为实物填充初始数据,this确保每个方法都知道自己在操作哪个对象。理解这四者的关系,就理解了 Java 面向对象编程的基石。

💡 关键提醒:始终牢记引用与对象的分离——一个引用变量只是一个"遥控器",多个遥控器可以指向同一个对象;构造方法的第一职责是初始化而非创建;在构造方法和 Setter 中坚持使用this.字段 = 参数的命名惯例,可以有效避免字段遮蔽带来的隐蔽 Bug。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 3:52:26

Redis分布式锁进阶第三十篇

Redis分布式锁进阶第三十篇&#xff1a;全系列终章大结局 企业级统一落地SOP归档 架构分级永久稳跑收官总结一、本篇前置衔接从第一篇原生锁手写入门&#xff0c;到第二十九篇监控运维可视化搭建&#xff0c;历经三十篇、全套完整链路打磨。前面二十九篇&#xff0c;覆盖基础…

作者头像 李华
网站建设 2026/5/14 3:52:18

DDR4信号完整性设计:从眼图验证到差分串扰抑制实战

1. 高速PCB设计工具与信号完整性挑战&#xff1a;从DesignCon 2015谈起 如果你是一名硬件工程师&#xff0c;尤其是负责过DDR内存布线、高速串行链路或者复杂背板设计&#xff0c;那你一定对信号完整性问题又爱又恨。爱的是&#xff0c;解决一个棘手的串扰或时序问题带来的成就…

作者头像 李华
网站建设 2026/5/14 3:50:03

Caveman Soul Optimizer:为AI助手注入“原始人灵魂”,节省70% Token成本

1. 项目概述&#xff1a;为AI助手注入“原始人灵魂”的优化器最近在折腾一个名为OpenClaw的AI助手框架时&#xff0c;我遇到了一个几乎所有AI应用开发者都会头疼的问题&#xff1a;API调用成本。尤其是当你让AI助手进行复杂的、多步骤的任务规划与思考时&#xff0c;它内部产生…

作者头像 李华
网站建设 2026/5/14 3:49:03

命令行光标增强:打造高效开发者的终端信息提示器

1. 项目概述&#xff1a;一个为开发者量身定制的命令行光标增强套件如果你是一名长期与终端打交道的开发者&#xff0c;无论是进行系统运维、后端开发&#xff0c;还是日常的版本控制操作&#xff0c;命令行界面&#xff08;CLI&#xff09;就是你最亲密的战友。然而&#xff0…

作者头像 李华
网站建设 2026/5/14 3:46:04

Java程序员找不到工作别都怪行情!

最近很多粉丝朋友私信我说&#xff1a;熬过了去年的寒冬却没熬过现在的内卷&#xff1b;打开Boss直拒一排已读不回&#xff0c;回的基本都是外包&#xff0c;薪资还给的不高&#xff0c;对技术水平要求也远超从前&#xff1b;感觉Java一个初中级岗位有上千人同时竞争&#xff0…

作者头像 李华
网站建设 2026/5/14 3:45:07

AI代码变更查看器:透视Claude Code修改过程,提升开发协作效率

1. 项目概述&#xff1a;为什么我们需要一个AI代码变更查看器&#xff1f;如果你和我一样&#xff0c;在日常开发中深度依赖像Claude Code这样的AI编程助手&#xff0c;那你一定遇到过这个痛点&#xff1a;AI助手噼里啪啦一顿操作&#xff0c;文件被修改了&#xff0c;但你却很…

作者头像 李华