Java面向对象入门:彻底理解类、对象与属性的关系

发布时间:2026/10/12 11:31:33

Java面向对象入门:彻底理解类、对象与属性的关系 聊一聊Java面向对象里最基础、但也是最容易绕晕的三个词类、对象、属性。很多初学者会陷入一个怪圈概念背得滚瓜烂熟闭着眼睛都能说出“类是抽象的概念对象是具体的事物”可一打开编辑器写代码还是不知道从哪下手。这一篇不打算讲得太理论而是想站在一个写Java写了好几年的从业者角度把这三个概念掰开揉碎结合代码、内存状态、踩坑经验带你真正理解它们各自扮演什么角色。目标是看完之后你能独立写一个包含类、对象、属性的小程序并且清楚每一步为什么这么写。1. 先搞明白一件事类是图纸对象才是房子1.1 类定义了“有什么”和“能干什么”对象才真正占内存“类”这个词听起来很抽象但本质上它就是一张图纸。你可以把它理解成房子的设计图图纸上画了客厅、卧室、厨房、卫生间标注了它们的位置和大小但图纸本身不能住人它只是一份规范、一个模板。真正能住进去的是按图纸盖出来的某一套具体房子。对应到Java代码里一个类大概长这样public class Person { String name; int age; void introduce() { System.out.println(我叫 name 今年 age 岁); } }这里Person就是图纸。name、age是图纸上标注的“设施”描述一个“人”应该有哪些数据introduce()方法描述这个“人”能做什么行为。请注意当你只写了这个类、没有执行new的时候整个程序里并不存在一个叫“张三”或者“李四”的人。类只会静静待在那里不占实际的数据空间。真正有血有肉的“人”是后面执行new Person()之后才出现的。同一张图纸可以盖出无数套房子同一个类也可以创建出无数个对象。这是理解类和对象关系最关键的一步类是定义对象是实例。我见过不少初学者把“为一个属性赋值”理解成“给类赋值”这就是把类和对象混在一起了。举个例子你让Person类里的name等于“张三”听起来没什么问题但类自己没有存储数据的空间数据只能存到某个具体的对象身上。所以你得先有对象才能在上面放数据。1.2 new关键字对象是怎么被“造”出来的Java里创建对象的标准写法是new加构造方法最常见的就是无参构造比如Person p1 new Person();这一行代码很多人会背但要问它到底做了几件事能说清的人就不多了。我拆开来解释一下new Person()在堆内存Heap中划出一块空间这块空间就是这个对象本身。Person类里定义的name、age都会出现在这块空间里并且会被赋予默认值。赋值给p1p1是一个引用类型的变量它本身不是对象它是贴在对象上的一个“门牌号”。通过p1就能找到堆里那个真正的对象。分号结束对象创建完成p1此刻已经指向一块真实存在的内存区域。所以当你再写Person p1 new Person(); p1.name 小明; p1.age 18; Person p2 new Person(); p2.name 小红; p2.age 20; System.out.println(p1.name); // 输出小明p1和p2是两个完全独立的对象堆里有两块互不相干的内存空间。给p2.name赋成“小红”完全不会影响p1.name。这个“每个对象各自持有自己的数据”的特性就是实例属性的本质。注意在初学阶段不要过于纠结new背后的底层细节但一定要记住“不使用new就没有对象”。很多空指针异常追到根上都是因为只声明了引用变量没有真正创建对象。1.3 从需求到类名词就是属性动词就是方法理解面向对象有一件事我觉得比死记“封装、继承、多态”更有用就是拿到一个需求时怎么把现实世界翻译成类。我习惯用两条非常朴素的规则找名词名词通常是类或属性。找动词动词通常是方法。比如需求是“做一个员工管理系统”。里面会出现“员工”“部门”“工资”“考勤”这些名词。“员工”就可以抽象成一个类“工号”“姓名”“工资”这些细节属性就是名词下的细分数据“入职”“离职”“调薪”这些动作就是方法。再比如“设计一个简单的购物车”。“购物车”是类“商品”“数量”“价格”是属性“添加商品”“删除商品”“计算总价”是方法。用这种方式入门你不需要去背“什么是抽象”而是直接参与了一次抽象过程。等你在练习中反复做这种“名词提取”的动作之后再回头看“类是对现实事物的抽象”这句话就会觉得它说的就是你已经做过的事。2. 属性是对象的“状态记录”先分清这几点2.1 属性在代码里的位置和写法属性也叫成员变量或字段field它是一个对象用来记录自己状态的数据。在Java里属性的声明位置非常讲究必须写在类的花括号内、方法的外面。public class Student { public String name; // 属性学生的姓名 public int score; // 属性学生成绩 public boolean pass; // 属性是否及格 }这里的name、score、pass就是Student对象的属性。以后每创建一个Student对象它都会自带这三个“空位”由你来填充具体数据。属性的声明语法并不复杂访问修饰符 数据类型 变量名后面可以跟初始值也可以不跟。访问修饰符暂时可以理解成“外部代码能不能直接碰到这个属性”public就是公开的、谁都能碰private就是私有的、只有类内部能碰。后面第4章会专门展开。我在带新人的时候经常发现一个现象有人把属性声明在了方法内部然后抱怨“为什么其他方法用不了这个变量”。原因很简单写在方法内部的变量叫局部变量它只属于那个方法属性必须在类这个层级声明才能被类里的所有方法共享。2.2 不赋值会怎样属性默认值速查表Java和很多语言不一样当你创建一个对象时所有属性会自动获得一个默认值。这个特性会在写代码时悄悄影响你。数据类型默认值int、short、byte0long0Lfloat0.0fdouble0.0booleanfalsechar\u0000引用类型String、数组、对象等null也就是说如果你创建了一个Student对象但没有给score赋值直接打印出来会是0不是报错。如果你创建了一个Student对象但没给name赋值打印出来会是null。初学者很容易在这里迷糊看到输出结果是null或者0第一反应是“哪里写错了”。但实际上程序完全没毛病只是对象创建之后你忘了赋值。这里我有一个非常实用的排查习惯看到null先别急着查代码逻辑先问自己一句“这个对象创建之后我真的给它赋过值吗”尤其要注意String类型的默认值是null不是空字符串。null意味着这个引用不指向任何对象如果你在这个引用上调方法比如name.length()程序会直接抛出NullPointerException。这个异常大概是Java初学者见得最多、最头疼的异常了根子多半就出在“引用类型属性没赋值”。2.3 属性与局部变量90%的新手在这上面翻车属性成员变量和局部变量是两回事但很多人会因为它们在写法上太像而搞混。我把它们的核心差别整理成一张表对比项属性局部变量声明位置类内、方法外方法内、参数列表、代码块内作用范围整个类的所有方法都可以访问只能在声明它的方法或代码块内使用默认值有默认值0、false、null等没有默认值必须先初始化再使用内存位置随对象存储在堆中方法调用时在栈中分配方法结束就释放生命周期和对象一样长只在方法执行期间存在看一个典型例子public class Demo { int a; // 这是属性有默认值0 public void test() { int b; // 这是局部变量没有默认值 // System.out.println(b); // 编译错误变量b可能尚未初始化 } }注释掉的那行如果放出来编译器会直接报错。原因很简单局部变量没有默认初始化机制你不给它赋值它连一个合法的初始状态都没有编译器只能拒绝编译。而属性因为是对象的组成部分对象一创建就分配了内存系统会自动初始化所以哪怕你不赋值也能正常读取默认值。这里补充一个我踩过很多的“坑”在方法内部声明了一个和属性同名的变量然后以为自己在操作属性实际上操作的是局部变量。这种现象叫“变量遮蔽”它是下一章要讲的this关键字存在的一个重要原因。3. 手写一个完整示例图书类的定义、实例化与使用3.1 先写Book类定义三个属性一个方法概念讲再多不如手写一遍。这个例子我选“图书”作为建模对象因为它足够简单又非常贴近生活。public class Book { String title; // 书名 String author; // 作者 double price; // 价格 void showInfo() { System.out.println(书名 title); System.out.println(作者 author); System.out.println(价格 price); } }这个Book类做的事情很纯粹定义了所有书都共有的三个属性——标题、作者、价格还有一个方法showInfo()负责把这本书的信息打印出来。注意现在的Book只是一张“图纸”。它告诉Java虚拟机将来如果创建图书对象每个对象都会有title、author、price三个数据位以及一个showInfo()功能。但此时此刻程序里还没有任何一本具体的书。3.2 创建两个对象分别操作属性下面建一个BookTest类作为程序入口在main方法里创建图书对象public class BookTest { public static void main(String[] args) { Book book1 new Book(); book1.title Java编程入门; book1.author 某作者; book1.price 59.9; Book book2 new Book(); book2.title 代码整洁之道; book2.author 某作者; book2.price 69.0; book1.showInfo(); System.out.println(----------); book2.showInfo(); } }这个程序跑起来之后控制台会分别打印两个对象各自的信息。整个过程可以分成几个关键动作Book book1 new Book();在堆内存中创建了一个Book对象并把引用交给book1变量。book1.title Java编程入门;通过点号.访问book1引用指向的那个对象把它的title属性设置为“Java编程入门”。book1.showInfo();通过点号调用对象上的方法。点号.在这里可以理解成“的”。book1.title就是“book1这本书的书名”book1.showInfo()就是“调用book1这本书的展示信息功能”。这个“的”字理解是很多人快速上手对象访问的关键。一个值得强调的点book1和book2虽然是同一个类创建出来的但它们的属性值彼此独立。我给book1.title赋值不会让book2.title也跟着变。如果想让所有图书共享同一个值那就得用第4章讲的static静态属性了。3.3 为什么属性“打印出来是null”两种常见情况很多人把代码敲完运行一看输出的title是null瞬间慌了。我见过的情况里九成以上是下面这两种第一种创建对象后忘了给属性赋值。比如只写了Book book1 new Book();就直接调用showInfo()。这时候title、author都是nullprice是0.0程序正常输出非常具有迷惑性。第二种在方法内部声明了同名变量把属性“遮住了”。比如下面这段故意写错的代码public class Book { String title; void setTitle(String title) { title title; // 你以为在给属性赋值其实参数赋值给了自己 } }setTitle(String title)里的title是参数是一个局部变量。方法内部写title titleJava实际执行的是“把参数title的值赋给参数title”类的title属性根本没有被触碰。于是外部调用setTitle之后属性还是null。这种问题用肉眼看很难发现因为它完全不报错。解决它的标准方案就是this关键字这个我们下一章详细说。这里先留个印象如果你发现属性怎么改都“没反应”先看看是不是有同名变量在捣乱。4. 把属性管起来封装、this与静态属性4.1 直接暴露属性的风险以银行账户为例前面的例子包括很多教材里的早期代码都会把属性声明成public然后在外部直接用点号赋值。这种写法对理解概念有帮助但放到真实项目里它就是一个隐患。我给你看个特别直观的例子public class BankAccount { public double balance; // 余额 }因为balance是public的任何外部代码都可以随便改它BankAccount account new BankAccount(); account.balance -10000; // 业务上完全不合理但代码能跑一个银行账户的余额变成负数这在真实业务里是不可接受的。问题不在于Java语法允不允许而在于直接把属性暴露出去就失去了对数据操作的管控能力。解决办法就是封装——把属性设为private对外提供专门的方法来读写public class BankAccount { private double balance; public void deposit(double amount) { if (amount 0) { balance amount; } } public double getBalance() { return balance; } }现在外部代码没法直接写account.balance -10000了因为它看不到balance这个属性。要存钱只能通过deposit()方法而方法内部可以加校验——金额小于等于0就直接忽略不改变余额。封装的好处远不止“防止非法数据”它还给未来的修改留了空间。比如哪天银行改了规则存钱超过一定金额要收手续费你只需要改deposit()方法内部外部调用代码一行都不用动。这种“内部多变、外部稳定”的工程思维才是封装真正的价值。4.2 this关键字的正确用法解决同名遮蔽回到第3章那个问题public class Book { String title; void setTitle(String title) { title title; // 属性没被赋值 } }要让它正确工作就得告诉Java“左边的title是当前对象的属性右边的title是参数”。这个“当前对象”就用this来表示public class Book { String title; void setTitle(String title) { this.title title; // this.title指的是对象的属性 } }this的含义就是“当前对象”。在方法内部this.title表示“当前对象的title属性”title则表示“参数列表里的title变量”。这两个名字用同一个单词但意义完全不同this把它们区分开了。有同学会问如果参数名不叫title是不是就不用this了比如void setTitle(String newTitle) { title newTitle; }这样确实能避免歧义很多老代码也这么写。但this仍然是Java里非常重要、绕不开的语法构造方法里给属性初始化、方法里调用当前对象的其他方法、链式调用等场景都会用到。尤其在构造方法中最标准的写法就是public class Student { private String name; private int age; public Student(String name, int age) { this.name name; this.age age; } }这里的this.name name会把外部传入的name参数赋值给“当前正在创建的那个对象”的name属性。没有this构造方法里的赋值就是一句空话。实操心得判断自己是不是踩了“同名遮蔽”的坑有个很有效的验证方法——把参数名临时改成别的名字比如newName。如果改名之后问题消失说明就是遮蔽问题如果问题还在再往下查别的原因。4.3 static属性与实例属性共享和独立要分清前几章的属性每个对象各存一份这叫实例属性。Java还有一种属性属于类而不是某个具体对象要用static修饰叫静态属性。两者最大的区别在于“共享范围”实例属性每个对象一份彼此不共享book1.title和book2.title互不影响。静态属性整个类只有一份所有对象共享随便哪个对象改了它所有对象看到的都是修改后的值。看一个经典例子public class Counter { static int totalCount 0; int instanceValue 0; public Counter() { totalCount; } } Counter c1 new Counter(); Counter c2 new Counter(); System.out.println(Counter.totalCount); // 输出2创建了c1和c2两个对象构造函数每次执行时都会让静态属性totalCount加1所以最终结果是2。它统计的是“一共创建了多少个Counter对象”。而instanceValue每个对象各自一份完全可以出现c1.instanceValue是0、c2.instanceValue是100的情况。访问静态属性时规范做法是用类名.属性名System.out.println(Counter.totalCount);用c1.totalCount虽然也能访问但不推荐因为会让人误以为它是c1实例独有的数据实际上它是共享的。还有一类错误特别经典就是静态方法里访问实例属性public class Demo { int a 1; static int b 2; // 静态方法里不能直接访问实例属性 public static void staticMethod() { // System.out.println(a); // 编译错误 } }原因是静态方法不依赖任何对象就能执行。可能整个程序一个Demo对象都没创建那怎么会有a呢所以为了让代码总是合法Java直接规定静态方法不能直接访问实例属性。反过来实例方法访问静态属性是允许的因为实例方法执行时必然有对象存在静态属性又总是存在于类中。5. 新手避坑指南四个高频错误与一次排查实录5.1 四个最容易踩的坑附对照表把前面讲的内容收拢一下初学者写类、对象、属性时我见得最多的错误是下面四个错误类型现象原因正确做法对象没创建就使用NullPointerException只声明了引用变量没执行new先new出对象再操作属性和局部变量同名属性永远是默认值方法内同名变量遮蔽了属性用this指代属性静态方法访问实例属性编译失败静态方法不依赖对象实例属性依赖对象改为实例方法或在静态方法里先创建对象属性直接公开数据可以被随意改坏缺少封装意识设为private提供getter/setter第一条值得展开一下因为NullPointerException几乎伴随每个Java初学者的成长。看这个例子Book book; book.title Java编程入门; // 运行时报空指针这里book只是一个引用变量它没有指向任何对象所以book.title根本没有真正的对象可以操作。正确写法是Book book new Book();。这句话我建议每个新手都当作咒语一样记住先new再点属性。5.2 一次“名字变null”的排查全过程之前带一位正在练手写“学生信息管理系统”的开发者排查问题过程很有代表性。他写了一个Student类定义了name和score两个属性然后模拟录入两个学生最后打印名单。结果运行时两个学生的名字全是null而分数却能正常显示。我当时没有直接告诉他答案而是陪他走了一遍排查流程第一步看类定义。确认name和score都声明在类内部、方法外部位置没有问题。第二步看对象创建。main方法里确实用了new Student()也执行了。对象存在不是空指针问题。第三步看赋值代码。他写的是student.setName(张三);于是重点进入setName方法public void setName(String name) { name name; }问题就在这一行。这里有两个name一个是参数name一个是类属性name。由于方法内部同名变量的优先级更高name name实际上是把参数赋给了参数自己属性name连碰都没被碰到。他给分数设置的setScore方法没有这个问题是因为参数名写的是int score吗不关键是他的setScore里用了this.score score。对他自己都没注意到两个setter一个用了this、一个没用。修复方式就是加上thispublic void setName(String name) { this.name name; }运行后名字正常显示。这个案例特别典型的地方在于没有报错没有异常只有静悄悄的错误赋值。所以这里我要再次强调那个验证技巧当你怀疑“属性为什么没被赋值”时临时改一下方法参数的名字如果问题消失几乎可以肯定是this缺失导致的遮蔽。5.3 给初学者的学习建议用模型思维代替死记硬背很多初学者问我面向对象到底怎么学才快我的回答通常不是“多背概念”而是“多画模型”。我有一套自己常用的方法说出来大家可以参考。第一把类想象成“表格模板”把对象想象成“表格里的一行数据”。比如一个Student类就像一张学生信息表的表头姓名、学号、成绩。每条具体的new Student()创建出来的对象就是表格里新加的一行记录。这样理解属性就非常直观——只属于这一行自己的数据。第二从身边找实体来练习建模。不要一开始就写“员工管理系统”“电商系统”这种大项目先从“我自己”开始Person类、属性有姓名、年龄、身高、体重方法有吃饭、跑步、睡觉。再写“我的电脑”类品牌、型号、内存大小方法有开机、关机。写三五个这种小类之后你对“从哪里提取属性、从哪里提取方法”的感觉就会完全不同。第三故意写错再改回来。学习Java的库里不常见的做法是“创造Bug”。“这个this加了会怎样不加又会怎样”“这个属性声明成static会发生什么”——亲手试出来的经验远比看十遍书来得深刻。最后说点个人体会吧。我刚开始写Java的时候也纠结过长篇大论的理论后来发现面向对象与其说是一门语法课不如说是一种看待世界的角度把一类事物的公共特征抽象出来就是类把它具体化成一条条可有不同状态的数据就是对象而属性就是每个对象如实记录自己的那一份状态。如果你现在也被类、对象、属性这三个词绕得头疼别急找个小例子敲一敲跑一跑故意踩几个坑再填回来用不了几次你就会发现这些东西其实特别自然。这也是我把这次的排查过程和踩坑经验全部记录下来的原因希望对你正在写的代码有一点实际的帮助。
延伸阅读

更多相关文章

2026/10/12 11:31:33

从零搭建代码评审自动化流程:open-code-review 实战指南

1. 从零搭建一套代码评审自动化流程,我为什么盯上了 open-code-review代码评审这件事,做过的都懂。项目小的时候,两三个人互相看看代码,提两句意见,也就过去了。可一旦团队规模上来,分支多、提交频繁、评审…

2026/10/12 11:31:33

Windows PyCharm直连Linux Hive可行性诊断指南

1. 这不是“连个数据库”那么简单:PyCharm在Windows上直连Linux Hive的真实挑战很多人看到标题第一反应是:“不就是配个JDBC驱动,填个URL,点一下Test Connection?”——我去年在某高校实验室带一个数据分析实训项目时&…

2026/10/12 11:31:33

基于卷积神经网络的农作物病虫害识别系统:Python源码与实战指南

简介:这份资源是面向计算机相关专业学生与项目实战学习者的农作物病虫害识别检测系统,基于深度学习卷积神经网络实现,可作为毕业设计、课程设计或期末大作业的高分参考方案。项目围绕病虫害图像数据集展开,涵盖图像尺寸标准化、降…

2026/10/12 12:31:50

Spring AI Alibaba停更后Java AI工程化实战指南

1. 这不是“Java不行了”,而是生态演进的正常快照最近在几个技术社区刷到标题为“Spring AI Alibaba已停更了,Java还有希望吗?”的讨论帖,点进去一看,多数是刚接触AI工程化的新手开发者,看到某个依赖包的Gi…

2026/10/12 12:31:50

JMH微基准测试实战:从线上事故到性能优化

1. 为什么我们需要JMH:从一次线上事故说起很多Java开发者对性能测试的理解还停留在“写个循环,记录开始和结束时间,一减就完事”的阶段。我早期也这么干过,直到有一次在某个数据处理模块的优化中,我信心满满地告诉团队…

2026/10/12 12:26:50

客户拜访记录散、乱、丢?企业级客户拜访沉淀的完整实操指南

客户拜访结束后,很多人都会遇到同一个尴尬:明明聊了很多,回到公司却只记得零散几句。客户提过的预算、决策人、竞品情况、成交顾虑,散落在微信、纸质笔记、手机录音和个人记忆里。更麻烦的是,这些信息往往只留在销售个…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑