在Java中,数据类型分为基本类型和引用类型,两者的存储方式和比较逻辑完全不同,很多新手容易混淆==和equals的用法,导致比较结果不符合预期。了解两者的差异是写出正确比较逻辑的基础。

Java基本类型的特点与比较方式
Java的8种基本类型分别是byte、short、int、long、float、double、char、boolean,它们直接存储数值本身,不需要在堆上分配对象空间。比较基本类型时,直接使用==运算符即可,该运算符会比较两个基本类型变量的实际值是否相等。
以下是基本类型比较的示例代码:
public class PrimitiveCompareDemo {
public static void main(String[] args) {
int a = 10;
int b = 10;
int c = 20;
// 基本类型用==比较数值
System.out.println(a == b); // 输出true
System.out.println(a == c); // 输出false
double d = 3.14;
double e = 3.14;
System.out.println(d == e); // 输出true
char f = 'a';
char g = 'a';
System.out.println(f == g); // 输出true
}
}对象比较的两种方式
对象是引用类型,变量存储的是对象在堆内存中的地址,不是对象本身的内容。比较对象时通常有两种方式:==运算符和equals方法,两者的作用完全不同。
==运算符比较对象
对于对象来说,==运算符比较的是两个引用变量是否指向同一个对象,也就是内存地址是否相同,而不是对象的内容是否相等。
示例代码如下:
public class ReferenceCompareDemo {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
// ==比较对象地址,两个不同对象的地址不同
System.out.println(s1 == s2); // 输出false
String s3 = s1;
// s3和s1指向同一个对象
System.out.println(s1 == s3); // 输出true
}
}equals方法比较对象
equals方法是Object类定义的方法,默认实现和==的作用一致,比较对象地址。但很多类会重写equals方法,用来比较对象的内容是否相等,比如String、Integer等包装类都重写了该方法。
String类使用equals比较内容的示例:
public class EqualsCompareDemo {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
// String重写了equals,比较字符串内容
System.out.println(s1.equals(s2)); // 输出true
Integer i1 = 100;
Integer i2 = 100;
// Integer也重写了equals,比较数值
System.out.println(i1.equals(i2)); // 输出true
}
}自定义对象的比较注意事项
如果是自定义的类,默认继承Object的equals方法,比较的还是对象地址。如果需要比较自定义对象的内容,就需要手动重写equals方法,同时建议重写hashCode方法,保证两个相等的对象hashCode也相同,符合集合类的使用规范。
自定义类重写equals和hashCode的示例:
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 重写equals方法比较内容
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
User user = (User) obj;
return age == user.age && name.equals(user.name);
}
// 重写hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class CustomObjectCompare {
public static void main(String[] args) {
User u1 = new User("张三", 20);
User u2 = new User("张三", 20);
// 重写equals后比较内容
System.out.println(u1.equals(u2)); // 输出true
// 未重写时这里会输出false
}
}常见比较场景总结
可以通过下面的表格快速区分不同场景的比较方式:
| 比较场景 | 推荐方式 | 说明 |
|---|---|---|
| 基本类型数值比较 | == | 直接比较存储的数值 |
| 判断两个引用是否指向同一对象 | == | 比较对象的内存地址 |
| 包装类、String等内容比较 | equals | 这些类已重写equals比较内容 |
| 自定义对象内容比较 | 重写equals后使用equals | 默认equals比较地址,需手动重写 |
需要注意的是,使用equals方法时要先判断对象是否为null,避免空指针异常,比如if (obj != null && obj.equals(target))这样的写法更安全。另外包装类的缓存机制也会影响比较结果,比如Integer在-128到127范围内会复用缓存对象,此时==比较也可能返回true,但超出范围就会返回false,所以比较包装类数值时优先使用equals更稳妥。