Java自定义类是为业务建模的引用类型,封装数据与行为,需明确职责、合理命名、私有字段加getter/setter、构造器初始化、重写equals/hashCode/toString、优先不可变设计。

Java 中的自定义类就是你为自己需要的数据建模而写的引用类型——它不是语言内置的 String 或 ArrayList,而是你定义的、能表达业务含义的类型,比如 Student、BankAccount 或 Product。关键在于:它封装数据(字段)和行为(方法),并通过 new 创建对象实例,所有实例都是堆内存中的引用。
明确职责,从现实概念出发设计类
写一个自定义类前,先问自己:这个类代表什么?它应该“知道什么”、又该“做什么”?比如设计 Book 类,核心属性可能是书名、作者、ISBN 和库存数量;它该提供的行为可能包括“借出一本”“补货”“判断是否缺货”。避免把不相关的功能塞进去,一个类只聚焦一个清晰的职责。
- 用名词命名类(
Order、PaymentGateway),属性用名词(price、status),方法用动词(cancel()、calculateTotal()) - 优先使用
private字段 + 公共 getter/setter 控制访问,而不是直接暴露字段 - 在构造器里完成必要初始化,比如不允许
name为null,就加校验逻辑
合理使用构造器与方法,体现对象生命周期
构造器不只是分配内存,它是对象诞生的第一道逻辑关口。你可以重载多个构造器,提供不同粒度的初始化方式;也可以用静态工厂方法(如 Book.of(title, author))提升可读性或复用性。方法则应围绕对象的状态变化展开——例如 withdraw(double amount) 不仅减余额,还应检查余额是否足够,并抛出有意义的异常。
- 构造器中避免复杂逻辑或外部依赖(如数据库调用),保持轻量
- 方法尽量保持单一目的,比如
validate()只校验,不修改状态;saveToDB()只持久化,不参与业务规则判断 - 返回
this支持链式调用(如user.setName("A").setAge(25)),但需谨慎,别破坏不变性
重视 equals()、hashCode() 和 toString()
这三个方法直接影响对象在集合中的行为和调试体验。默认实现基于内存地址,对大多数业务对象没意义。比如两个 Student 对象,只要学号相同,就应视为相等;打印时也应显示关键字段而非一串哈希码。
立即学习“Java免费学习笔记(深入)”;
- 重写
equals()必须同时重写hashCode(),否则放进HashSet或作为HashMap键会出错 - 用
Objects.equals(a, b)和Objects.hash(...)简化安全比较和哈希计算 -
toString()建议包含类名和所有重要字段,格式清晰(如"Student{id=101, name='Li', grade=89}")
用封装和不可变性增强可靠性
字段私有化只是第一步。进一步可让类不可变:所有字段 final,构造器全参数初始化,不提供 setter,且对外返回的集合/数组做防御性拷贝(如返回 new ArrayList(this.items))。不可变对象线程安全、易于测试、不易被意外修改。
- 若必须可变,确保修改操作有明确契约——比如
addItem()返回布尔值表示是否成功 - 对传入的可变参数(如
List)做防御性复制,防止外部修改影响内部状态 - 考虑用记录类(
record)简化不可变数据载体,Java 14+ 支持,自动实现equals/hashCode/toString
不复杂但容易忽略——自定义类的价值不在语法多高级,而在能否准确映射问题域、降低理解成本、支撑后续扩展。写的时候多想一句“别人读这段代码时,能不能一眼看懂它是什么、怎么用、有什么限制”,就是好设计的开始。


















