1. Java中的比较
1.1 比较操作符 ==
- 对于基本类型(如int, double, boolean等),
==比较的是值。 - 对于引用类型(如String, Integer等),
==比较的是内存地址(即是否指向同一个对象)。
1.2 equals方法
equals方法是Object类的方法,默认行为与==相同,比较内存地址。- 很多类重写了
equals方法,使其比较对象的内容。例如String、包装类等。 - 重写
equals方法时,通常也要重写hashCode方法。
1.3 字符串比较
- 字符串常量池:字符串字面量会被放入常量池,相同的字面量会指向同一个对象。
- 使用
new String("hello")会创建新的对象,与常量池中的对象不是同一个。
1.4 包装类的比较
- 包装类重写了
equals方法,比较的是值。 - 但由于缓存机制,在缓存范围内的值使用
==可能会返回true,但超出范围则会返回false。因此,包装类的比较推荐使用equals。
1.5 Objects.equals方法
- 这是Java 7引入的工具方法,用于安全地比较两个对象。
- 它先检查两个对象是否同一个引用(包括都为null),然后检查非null并调用第一个对象的
equals方法。 - 优点:可以避免空指针异常。
2. 自动装箱与自动拆箱
2.1 基本概念
- 自动装箱:将基本类型自动转换为对应的包装类对象。例如:
Integer i = 10;等价于Integer i = Integer.valueOf(10); - 自动拆箱:将包装类对象自动转换为对应的基本类型。例如:
int n = i;等价于int n = i.intValue();
2.2 发生时机
- 赋值:
Integer i = 10;(装箱) 和int n = i;(拆箱) - 方法调用:将基本类型传递给包装类参数(装箱),反之亦然(拆箱)。
- 集合操作:集合中存储包装类,但可以添加基本类型(装箱),取出时也可以直接赋值给基本类型(拆箱)。
- 算术运算:包装类对象参与算术运算时会被拆箱。
2.3 注意事项
- 自动拆箱时,如果包装类对象为null,会抛出NullPointerException。
- 频繁的装箱拆箱可能会影响性能。
3. 包装类的缓存机制
3.1 缓存范围
-
Java为部分包装类提供了缓存机制,常用于数值范围在-128到127之间。
-
具体缓存范围:
- Byte:全部缓存(-128到127)
- Short:-128到127
- Integer:-128到127(可通过JVM参数
-XX:AutoBoxCacheMax扩展上限) - Long:-128到127
- Character:0到127
- Boolean:缓存了TRUE和FALSE
3.2 缓存的作用
- 提高性能:避免频繁创建和销毁对象。
- 节省内存:重复使用缓存的对象。
3.3 缓存实现原理
- 使用
valueOf方法时,会先检查值是否在缓存范围内,如果在则返回缓存对象,否则创建新对象。 - 例如
Integer.valueOf(int i)方法。
3.4 注意事项
- 使用
new关键字会创建新对象,绕过缓存。 - 在比较包装类时,使用
==比较可能会因为缓存而出现预期之外的结果,因此推荐使用equals方法。
4. 示例代码
4.1 比较示例
java
// 基本类型比较
int a = 100;
int b = 100;
System.out.println(a == b); // true
// 引用类型比较
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s3)); // true
// 包装类比较
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2); // true,在缓存范围内
System.out.println(i3 == i4); // false,超出缓存范围
System.out.println(i3.equals(i4)); // true
// 使用Objects.equals
System.out.println(Objects.equals(i3, i4)); // true
System.out.println(Objects.equals(null, i4)); // false
System.out.println(Objects.equals(null, null)); // true
4.2 自动装箱拆箱示例
java
// 自动装箱
Integer i = 10; // 等价于 Integer.valueOf(10)
// 自动拆箱
int n = i; // 等价于 i.intValue()
// 方法调用中的装箱拆箱
public void method(Integer i) { ... }
method(10); // 装箱,将10转换为Integer
public int method2() {
Integer i = 10;
return i; // 拆箱
}
// 集合中的装箱拆箱
List<Integer> list = new ArrayList<>();
list.add(1); // 装箱
int num = list.get(0); // 拆箱
// 算术运算中的拆箱
Integer a = 10;
Integer b = 20;
Integer c = a + b; // 先拆箱再装箱
4.3 缓存机制示例
java
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
System.out.println(a == b); // true
Integer c = Integer.valueOf(128);
Integer d = Integer.valueOf(128);
System.out.println(c == d); // false
// 使用new绕过缓存
Integer e = new Integer(127);
Integer f = new Integer(127);
System.out.println(e == f); // false
5. 最佳实践
- 基本类型比较使用
==。 - 引用类型比较使用
equals,并注意重写equals和hashCode方法。 - 包装类比较一律使用
equals,避免使用==。 - 使用
Objects.equals进行null安全的比较。 - 注意自动装箱拆箱的时机,避免不必要的性能损耗和空指针异常。
- 了解包装类的缓存机制,但不要依赖缓存范围进行编程(除非有明确规范)。
希望这份笔记能帮助你更好地理解Java中的比较、自动装箱拆箱和缓存机制。