常用类(Object、包装类、Math)
概述
Java 中 Object 是所有类的根类,其 equals / hashCode / toString 的约定直接影响对象在集合、比较和调试中的行为。包装类(如 Integer、Double)将基本类型封装为对象,支持自动装箱与拆箱,是集合和泛型中不可或缺的一环。Math 则提供常用的数学运算与常量。本节从「值语义」与「日常开发」两个角度介绍这三类常用 API。
Object 与通用方法
Object 的地位
在 Java 中,所有类(除 Object 自身)都直接或间接继承自 Object。因此,任何对象都可以调用 Object 提供的方法,例如 equals、hashCode、toString、getClass、clone、notify/notifyAll/wait 等。日常开发中最常需要重写的是 equals、hashCode 和 toString。
equals 与 hashCode 契约
equals 用于判断两个对象在「业务含义」上是否相等;hashCode 返回对象的哈希值,用于基于哈希的集合(如 HashMap、HashSet)。Java 规定:
- equals 相等 → 两对象的 hashCode 必须相等。
- equals 不相等 → hashCode 可以相等(哈希冲突),但最好尽量不同以提高散列性能。
- 重写 equals 时必须同时重写 hashCode,否则在 HashMap、HashSet 等集合中会出现「逻辑上相等但查不到」或「重复元素」等错误行为。
注意
重写 equals 时必须同时重写 hashCode,否则在基于哈希的集合中行为异常。
正确重写 equals
推荐步骤:先判断是否同一引用,再判断 null 和类型,最后按业务字段逐项比较。基本类型用 ==,引用类型用 Objects.equals 以安全处理 null。
import java.util.Objects;
public class User {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}this == o:同一引用直接返回 true,兼顾性能与一致性。getClass() != o.getClass():严格同类比较,子类与父类不算相等(若需要「子类与父类按字段相等」再考虑用instanceof等方案)。Objects.equals(name, user.name):避免对 name 做 null 判断,若两者都为 null 也返回 true。
正确重写 hashCode
参与 equals 比较的所有字段都应参与 hashCode 计算,且算法应保持一致。使用 Objects.hash(字段1, 字段2, ...) 即可满足约定且不易出错。
@Override
public int hashCode() {
return Objects.hash(name, age);
}若类不可变且 hashCode 计算较贵,可考虑缓存哈希值;一般业务类用 Objects.hash 即可。
重写 toString
toString 在打印、日志、调试时被广泛使用。重写后便于阅读,且 IDE 生成的「根据关键字段拼接字符串」的形式通常就够用。
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}提示
使用 IDE 的「生成 equals/hashCode/toString」功能可减少手写错误,生成后可根据业务稍作调整(例如排除某些字段)。
包装类(Wrapper Classes)
与基本类型的对应关系
Java 为每种基本类型提供了对应的包装类,用于在需要「对象」的场合(如集合、泛型)表示该类型的值。
| 基本类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
自动装箱与自动拆箱
自动装箱:基本类型自动转换为对应包装类对象。自动拆箱:包装类对象自动转换为基本类型。这是编译器提供的语法糖,本质仍是调用 valueOf 与 xxxValue()。
// 自动装箱:int -> Integer
Integer a = 100;
// 自动拆箱:Integer -> int
int b = a;
int sum = a + 200; // a 先拆箱再参与运算
// 集合中只能存放对象,泛型中使用包装类
List<Integer> list = new ArrayList<>();
list.add(1); // 自动装箱
int first = list.get(0); // 自动拆箱包装类的常用方法
- 与字符串互转:
Integer.parseInt("123")、Integer.toString(123),以及带进制或默认值的重载。 - 比较:包装类应使用
equals比较内容,不要用==(==比较的是引用,缓存范围内可能相等,但不可依赖)。 - 数值型通用方法:如
Integer.valueOf、Double.valueOf,以及各类型的xxxValue()拆箱方法。
// 字符串与数值互转
int num = Integer.parseInt("42");
String s = Integer.toString(42);
// 比较:用 equals,不要用 ==
Integer x = 128;
Integer y = 128;
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false(不同对象)警告
包装类比较内容时要用 equals,不要用 ==。== 比较的是引用,仅当 -128~127 等缓存范围内可能为同一对象,不可作为通用相等判断。
缓存与 valueOf
部分包装类(如 Integer)对一定范围内的值做了缓存,valueOf 可能返回缓存对象,因此同一数值在缓存内可能满足 ==,但代码不应依赖这一实现细节,相等性判断一律用 equals。
Math 工具类
Math 提供静态方法用于数学运算,无需创建对象。常用内容如下。
常用方法与常量
- 取整:
Math.round(四舍五入)、Math.floor(向下)、Math.ceil(向上)。 - 最值:
Math.max(a, b)、Math.min(a, b)。 - 幂与开方:
Math.pow(x, y)、Math.sqrt(x)。 - 随机数:
Math.random()返回 [0.0, 1.0) 的 double;需要整数范围时可结合乘法和强转,或使用Random类。 - 常量:
Math.PI、Math.E。
double a = 3.7;
Math.round(a); // 4
Math.floor(a); // 3.0
Math.ceil(a); // 4.0
Math.max(10, 20); // 20
Math.pow(2, 10); // 1024.0
Math.sqrt(4); // 2.0
// [0.0, 1.0) 的随机数
double r = Math.random();
// 例如生成 [1, 100] 的整数(需注意边界)
int rand = (int) (Math.random() * 100) + 1;信息
需要更灵活或线程安全的随机数时,推荐使用 java.util.Random 或 ThreadLocalRandom(JDK 7+),见 Oracle Java 文档 - Random。
注意事项
- equals/hashCode:参与 equals 的字段都要参与 hashCode;保持 equals 与 hashCode 一致,且不要依赖对象可变字段在生命周期内的变化来改变 equals 结果,否则在集合中会导致难以排查的 bug。
- 包装类:在集合与泛型中必须使用包装类;比较时用 equals,避免用
==;注意自动拆箱时的 NullPointerException(包装类为 null 时拆箱会抛 NPE)。 - Math:方法多为 double 入参和返回值,注意精度与强转;随机数若需整数或特定分布,优先考虑 Random / ThreadLocalRandom。
相关链接
- String 与字符串操作 — 字符串常用 API
- 泛型基础 — 集合与泛型中为何常用包装类
- 集合概述 — 集合中对象与 equals/hashCode 的关系
- Oracle Java 文档 - Object
- Oracle Java 文档 - Math