首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Java类用法详解:从封装到构造方法,彻底搞懂类的设计
📅 2026/9/9 9:16:29
✍️ 爱科研究院
👁 阅读 3,247
这期我们聊一个很基础、但几乎所有初学者都会卡壳的话题Java里的类到底该怎么用。我在带新人或者帮读者看代码的时候经常发现很多人不是不会写语法而是不知道一个类里该放什么、不该放什么更不明白为什么有些字段要私有、有些方法要公开。这次借着《Java程序设计(第3版)》里的“代码练习3:类的用法”我把类的设计思路、手写代码时的注意事项、还有我实际踩过的坑一次性讲清楚。不管你是刚学完语法准备做练习还是已经写了一阵子代码但总觉得自己的类“不太对劲”这篇都值得你花十分钟看完。1. 内容整体设计与思路拆解1.1 这个练习到底在练什么“类的用法”这个练习表面上是让你定义一个class、写几个字段和方法、再new一个对象出来调用一下。但如果你只做到这一步那和没练没什么区别。这个练习真正想让你掌握的是三件事如何把一个现实中的东西抽象成类的结构、如何控制外部对类内部数据的访问、如何通过方法而不是直接操作字段来改变对象的状态。我在实际带人的过程中发现很多初学者对类的理解停留在“字段就是变量方法就是函数把它们放在一起就是类”。这个理解不能算错但是太浅了。类真正的价值在于它把数据和操作数据的行为绑定在了一起而且通过访问控制符public、private这些告诉使用者哪些东西你可以碰哪些东西你不该碰。在这个练习里题目通常会给你一个具体的场景比如设计一个学生类、图书类或者银行账户类。你需要做的不是写一个“能跑就行”的版本而是要按照面向对象的思路把类的职责划分清楚把不该暴露的细节藏起来把该提供的接口做对。1.2 为什么类不是“变量函数”的集合这么简单我用一个生活化的例子来解释。你把手机递给别人用别人能操作的只有屏幕和几个按键他不会去动手机内部的电池和主板。这不是因为他不能拆开手机而是因为设计手机的人通过外壳和螺丝把内部细节保护起来了。类也是一样的道理。字段就是手机内部的元器件方法就是手机外部的按键和屏幕访问控制符就是那个外壳。如果你把字段全部设成public让外部代码随便改那就等于把手机主板露在外面谁都能碰一下。短期看没什么问题但一旦你的程序变大多个人协作开发这种“裸露字段”的设计会让代码迅速腐化。我见过太多练习作业学生把字段写成public然后在main方法里直接stu.score 100这样赋值。跑起来是没问题但一旦你需要在赋值的时候做校验比如分数必须在0到100之间你就得去所有赋值的地方改代码。而如果你通过一个setScore方法来做只需要改这一个方法就够了。这就是封装的意义也是这个练习想让你体会的东西。2. 核心细节解析与实操要点2.1 访问控制符的选择用不好就是给自己挖坑讲类的用法访问控制符是绕不开的坎。很多教材会给你一张表告诉你public、protected、默认包访问权限、private分别是什么意思。这里我不重复那套东西我只说我在实际编码里总结出来的经验默认情况下字段全部private方法尽量public需要被子类重写的方法才考虑protected。为什么字段要全部private我举个具体的场景。假设你写了一个图书类Book里面有一个字段int pages表示页数。如果它是public的那么另一个开发者在某个地方写了book.pages -100。编译器不会报错程序也能运行但显然一本页数为负数的书是有问题的。如果pages是private的你就必须在构造方法或setter里做校验非法值根本进不来。当然凡事有例外。有些场景下private反而给自己找麻烦。比如一个纯粹承载数据的类类似DTO、VO字段全私有、每个字段都要getter和setter这套写法其实很繁琐Java社区后来用Lombok的Data注解来解决这个问题。但这属于工程实践范畴作为练习你还是老老实实按规范来把字段私有化用方法对外提供服务。2.2 构造方法的设计不只是用来初始化数据构造方法是初学者最容易忽略的部分。很多人写类的时候只写一个无参构造方法或者干脆不写用系统默认的。但构造方法的用处远不止“新建对象时初始化字段”这么简单。它还是你控制一个类如何被创建的重要手段。我举个最常见的例子。你设计一个学生类Student有学号、姓名、班级三个字段。如果你只提供一个无参构造方法使用者可以new出一个什么都没有的学生对象然后慢慢往里面填数据。这没什么不好但如果你希望学号是必填的、且不可修改的就应该提供一个带学号参数的构造方法并且不为学号提供setter。这样学号只能在创建对象时设置一次之后谁都没法改。这是一个很经典的不变式设计。还有一个细节很多人不知道构造方法可以重载。你可以提供多个构造方法让使用者根据自己手头的数据选择用哪一个。比如new Student(s001)和new Student(s001, 张三, 软件工程1班)。重载不是“偷懒”而是一种常见的编码习惯目的是让类的使用方在不同场景下都有的选。2.3 方法设计里的“职责单一”有多重要我改代码的时候最怕遇到那种一个方法写了几百行、什么都干的方法。这种方法的特征是名字很大而通用比如processData()或者handleSomething()打开一看里面既处理业务逻辑又负责连接数据库还要更新界面显示。这种代码一旦出bug定位问题的时间是正常代码的三倍以上。在这个练习里你的方法大概不会复杂到写几百行但你要养成一个习惯一个方法最好只做一件事。比如一个图书类Library你要实现借书、还书、查询库存三个功能就应该拆成三个方法borrowBook(String bookId)、returnBook(String bookId)、queryStock(String bookId)。不要写一个manageBook(int operation, String bookId)然后根据operation参数去判断干什么。这种设计在练习阶段可能感觉无所谓但工作以后你会发现好的方法设计能省去你大量调试和重构的时间。我还会建议你写方法的时候注意方法名的可读性。命名是很主观的事但有一个通用的标准别人看到方法名大概能猜到它做什么。borrowBook比op1好getStudentScore比getData好。这个练习就是培养这种意识的最好时机因为题目足够简单你不需要分心处理逻辑复杂性可以刻意练习命名的规范性。3. 实操过程与核心环节实现3.1 从零开始设计一个图书管理类代码逐步手写我直接用一个实际练习题目来演示。假设题目要求你设计一个简单的图书类包含书名、作者、价格、库存数量四个属性并且能实现“查看图书信息”“修改库存”“判断是否有库存”这几个功能。拿着这个题目我们一步步来。第一步明确字段。书名和作者是字符串价格用double或BigDecimal库存数量用int。这些字段都应该设为private。为何价格不用float而用double这是个小细节float精度低价格运算容易出误差虽然练习题目中介不介意但习惯上我会用double。真实项目里我更建议用BigDecimal但那一套涉及写法篇幅太长练习阶段double够用。第二步设计构造方法。这里我提供两个一个全参构造方法用于一次性初始化所有字段一个无参构造方法先造一个空对象。写全参构造方法的时候直接给字段赋值就行但库存数量我建议做一下限制如果传入的数量小于0就抛一个IllegalArgumentException。第三步设计业务方法。showInfo()方法用于打印图书信息addStock(int count)和subStock(int count)分别用来增加和减少库存hasStock()用来判断库存是否大于0。注意subStock里必须检查如果当前库存不足应该提示还是抛异常我建议抛IllegalStateException因为“库存不足”属于状态非法不是参数非法。代码写出来大概是这样的public class Book { private String title; private String author; private double price; private int stock; public Book() { } public Book(String title, String author, double price, int stock) { this.title title; this.author author; this.price price; if (stock 0) { throw new IllegalArgumentException(库存数量不能为负数); } this.stock stock; } public void showInfo() { System.out.println(书名 title 作者 author 价格 price 库存 stock); } public void addStock(int count) { if (count 0) { throw new IllegalArgumentException(增加数量不能为负数); } this.stock count; } public void subStock(int count) { if (count 0) { throw new IllegalArgumentException(减少数量不能为负数); } if (count this.stock) { throw new IllegalStateException(库存不足当前库存 this.stock); } this.stock - count; } public boolean hasStock() { return this.stock 0; } public int getStock() { return this.stock; } }你看这个类外部没有任何直接操作stock字段的入口所有对库存的修改都必须通过addStock和subStock。这样一来非法数据进不来库存的约束逻辑也集中在类内部不会散落在各处。这就是封装的一个完整示范。3.2 编写测试代码验证类的行为类写完之后一定要写一个测试类来验证行为。哪怕题目没有要求你写测试我也强烈建议你在练习阶段就养成写测试代码的习惯。所谓测试代码不一定要用JUnit那一套一个带main方法的简单验证类就足够了。我的做法是新建一个BookTest类在main方法里创建Book对象依次调用那些业务方法然后用打印的方式观察结果对不对。比如先new一本库存为10的《Java核心技术》调用showInfo()看一眼输出调用addStock(5)后看看库存变成多少调用subStock(20)看看会不会抛出异常最后调用hasStock()看看返回什么。每一步都打印一条日志这样你能非常直观地看到对象状态是如何被一步步改变的。这一步的意义在于你不经过一次完整的验证就不知道自己写的类是否真的“能用”。很多人写完类在IDE里看到没有语法错误就觉得完事了结果运行的时候才发现逻辑有漏洞。与其等到面试或者项目里再暴露问题不如从练习阶段就老老实实做验证。3.3 用类图思维检查你的设计是否合理我发现很多初学者根本没听说过“类图”这个概念但类图恰恰是检验类的设计是否合理的最佳工具。IDE里有自动生成类图的插件比如IntelliJ IDEA里右键点击包名选择Diagrams就能看到当前项目里的类之间的关系。类图主要看什么东西一是看字段的状态字段都是private的吗有没有public字段直接露在外面二是看方法方法数量是否合理有没有某个类只有一个getter挨个返回字段却没有一个真正干活的业务方法三是看类之间的关联这个类是否依赖了它不该依赖的东西这里给大家一个简单自查表字段全部私有需要外部访问时通过getter获取需要外部修改时通过setter或业务方法修改。类的字段是否都有意义是否存在“这个字段其实根本用不到”的情况。方法是否都服务于这个类本身的核心职责有没有出现某个方法只是给别的类打工的情况。构造方法是否能保证对象创建后处于一个合法状态也就是创建出来的对象字段值不能自相矛盾。拿上面Book类来对照字段全私有方法都围绕图书信息和库存操作展开构造方法也做了非空数的约束。这个设计虽然简单但结构上是合格的。4. 常见问题与排查技巧实录4.1 为什么“对象为null”的错误总是出现在我以为自己已经初始化了之后写过Java的人应该都见过NullPointerException而且它几乎总是出现在最不应该出错的地方。我给一个最常见的场景你定义一个类里面有一个数组或集合字段但是没有在声明时初始化。你在类的方法里直接往这个数组的元素赋值编译不报错一到运行就崩。比如这段代码public class TestClass { private String[] items; public void setItem(int index, String value) { this.items[index] value; } }初看上去没毛病但items只是声明了并没有初始化它的值是null。this.items[index] value就是在往null对象上操作当然会抛空指针异常。解决办法有两种一种是在声明时就new String[10]另一种是在构造方法里初始化。我把这种问题叫“只挂号不看诊”——你告诉JVM有这样一个字段存在却没有真正把它创建出来。排查的方法是看到数组、集合或者自定义类型的字段先问自己一句这个字段到底有没有被new过这是所有Java初学者都会犯的经典错误练第一次类的练习时踩一次后面写任何类都会记得。4.2 方法参数和字段重名时值为null或0你忘了this还有一个频率很高的坑构造方法或setter方法里形参名和字段名一模一样然后直接赋值却没写this关键字。比如public class Person { private String name; public void setName(String name) { name name; // 这里给形参自己赋值字段完全没变 } }这段代码里的name name左边的name和方法参数name指向同一个变量它把参数的值赋给了自己类的name字段一点没动。结果就是setName之后你再去get得到的是null或者初始值。解决方式就一个在方法内部给字段赋值时必须加上this.写成this.name name。this在这里的含义是“当前这个对象”所以this.name表示对象自己的字段。这个知识点教材里一定讲过但因为它太基础很多人练习的时候根本没认真用直到出了问题才想起来。4.3 构造方法调用别的方法时对象还没完全创建好有一个比较进阶的坑面试里也经常被问到。如果你在构造方法里调用了另一个方法而这个方法依赖子类重写过的方法就会调用到子类的方法——但此时子类的字段还没初始化可能全是默认值或null。这属于构造方法设计层面的隐患。在练习阶段你大概率不会碰到这么复杂的情况但我要提醒的是构造方法里尽量只做字段初始化和参数校验不要调用带有业务逻辑的方法。如果你确实需要在创建对象的过程中做一些处理推荐用静态工厂方法或者专门的初始化方法而不是在构造方法里“隐式调用”一堆东西。4.4 常见错误速查表错误现象可能原因排查与修复字段通过方法修改后没有变化方法参数和字段重名且未使用this关键字检查方法内部赋值语句确认为字段加this运行到某行时报告无法识别变量变量作用域超出声明位置或变量名拼错确认变量声明位置检查作用域范围对数组或集合操作时报空指针异常字段声明后未初始化直接使用在声明处直接new或在构造方法中初始化new对象时提示参数数量和构造器不匹配构造方法形参与实际调用参数不一致检查构造方法签名和调用处参数列表打印对象时输出变成地址形式未重写toString方法为类添加toString方法并返回可读字符串编译器提示类的某个成员缺少访问修饰符未区分public/private等权限明确为每个成员添加合适的访问控制符4.5 给初学者的几个实操建议从练习中建立类的直觉最后说几个我自己带学生、带新人时反复强调的建议。第一个建议是每次写完一个类都尝试用一句话说清楚这个类的职责。比如“Book类负责存储图书基本信息并维护库存数量”。如果这句话说不清楚说明这个类的职责可能不够单一。第二个建议是不要跳过练习直接在IDE里点几下生成getter和setter。我明白生成代码高效但练习的时候手工写一遍能让你对哪些字段需要getter、哪些需要setter更有感觉。很多人的类之所以臃肿就是因为一键生成了所有getter和setter不管用不用全都暴露出去。第三个建议是试着给同一个类写两个完全不同的版本。第一个版本把所有字段都设为public不写任何校验第二个版本严格封装所有字段私有所有修改走方法。然后对比一下你会发现第二个版本虽然写起来麻烦但用起来更放心改起来也更方便。我个人在实际操作中还有一个比较笨、但确实有用的习惯。我会故意在测试代码里写一些“不合法”的操作比如给库存减少一个超过当前数量的值或者给一个本不该有负数的字段赋负值然后看我的类能不能拦住这些操作。能拦住说明类的设计是可靠的拦不住说明你的封装还没到位。这个习惯我保持了很多年后来做工程时写DAO层、Service层的类也是用这种思路来验证边界。把练习当成真正的项目来对待很多问题就会提前暴露在成本最低的时候。类的用法看似简单但它决定了你之后写所有Java代码的基本功。把封装、构造方法、方法设计和异常处理这几件事练扎实你后面学继承、多态、抽象类、接口的时候会顺很多。反过来如果这个练习只是把类和对象当“语法糖”一样应付过去那后面补概念的时候可能要花好几倍的时间。希望这一篇能把“类的正确用法”讲透你写练习的时候也按这个标准来要求自己。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/9 9:16:29
FPGA UDP通信模块设计:从零实现以太网高速数据传输
2026/9/9 9:16:29
信创环境下的OpenClaw部署:Chromium安装与配置实战
2026/9/9 9:11:28
Opencode本地AI编程助手:离线、可控、可审计的代码理解引擎
2026/9/9 10:11:38
模拟电路设计入门难?宝藏视频和仿真实践教你建立电路直觉
2026/9/9 10:11:38
数据挖掘实战:全球人均预期寿命影响因素分析与预测模型
2026/9/9 10:11:38
2026边缘计算设备选型指南:AI SoC与推理卡实战避坑
2026/9/9 10:11:38
企业AI平台选型避坑指南:POC、私有化部署与TCO实战经验
2026/9/9 10:11:38
营销技能树拆解:从用户洞察到数据复盘的实战方法论
2026/9/9 10:06:37
opencode:打造可定制、多模型的开源终端AI编程助手
2026/9/9 0:00:26
MHS模型硬件标准:让大模型像调用软件一样控制物理设备
2026/9/9 0:00:27
AI五大核心方向详解:从机器学习到大模型,零基础转行选哪条?
2026/9/9 0:00:27
从50行最小循环到生产级AI引擎:工程化改造全解析
2026/9/9 2:07:00
超人会飞不算本事:系统稳定依赖清晰规则与边界设计
2026/9/9 1:41:51
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
2026/9/9 5:25:52
基于CNN的调制信号识别:MATLAB实现时频图分类实战