Java--学习笔记

在Java编程中,Integer类的使用广泛,但也存在一些容易忽视的细节和陷阱。本文将深入讨论以下几个关键问题:

  1. equals()方法与==运算符的区别与联系。
  2. hashCode()方法的作用。
  3. 为什么重写equals()方法时必须重写hashCode()方法。
  4. Java中的“128陷阱”。
  5. 自动装箱与自动拆箱的细节。

1. equals()方法与==运算符的区别与联系

区别
  • ==运算符:

    • 基本数据类型:用于比较两个变量的值是否相等。例如,对于int类型的变量aba == b比较的是它们的值是否相同。
    • 引用数据类型:用于比较两个引用是否指向同一个对象。例如,对于Integer类型的变量aba == b比较的是它们是否指向堆内存中的同一个对象。
  • equals()方法:

    • Object类中的方法,用于比较两个对象的内容是否相等。默认情况下,equals()方法是比较对象的内存地址,效果与==相同。
    • 但是,很多类(如StringInteger等)都重写了equals()方法,使得它们能够比较对象的实际内容。例如,对于String类型的变量str1str2str1.equals(str2)比较的是它们的字符序列是否相同。
联系
  • 二者都可以用于比较两个对象,但它们比较的依据和结果可能不同。
  • 在比较基本数据类型时,通常使用==;在比较引用数据类型时,如果需要比较对象的内容,应使用equals()方法。
  • 对于Integer类型的变量,当值在-128到127范围内时,使用==比较可能会得到true的结果,因为这些值会被缓存并指向同一个对象。

示例代码:

public class Main {
    public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        Integer c = 200;
        Integer d = 200;
 
        System.out.println(a == b); // true,因为100在缓存范围内
        System.out.println(c == d); // false,因为200不在缓存范围内
 
        System.out.println(a.equals(b)); // true
        System.out.println(c.equals(d)); // true
    }
}

2. hashCode()方法的作用

        

1. hashCode() 的基本概念和作用

hashCode()Object 类中的一个方法,它返回一个整数值,这个值用于表示对象在内存中的哈希码。哈希码是通过特定的算法生成的一个整数值,它的目的是用来在哈希表(例如 HashMapHashSet)中快速定位对象存储位置。

为什么需要哈希码? 哈希表使用哈希码来决定对象存储的位置。哈希表的工作原理是通过哈希算法将键映射到一个特定的存储桶中,这样可以提高查找、插入、删除等操作的效率。在 HashMap 中,哈希码帮助确定对象存储的桶(或槽)的索引位置,从而在查找时能够迅速定位该对象。

2. hashCode() 和 equals() 之间的关系

hashCode()equals() 方法之间有一个重要的约定,它们的关系如下:

  • 对称性规则:如果两个对象通过 equals() 方法比较相等(即 obj1.equals(obj2) 返回 true),那么这两个对象的 hashCode() 方法返回的值也必须相等。

  • 不等不代表不相等:反过来,如果两个对象的 hashCode() 返回的值不同,那么这两个对象就一定不相等(即 obj1.equals(obj2) 必定返回 false)。但是如果它们的哈希码相同,也不意味着这两个对象就相等,可能出现哈希碰撞(hash collision)的情况。

3. 为什么需要这个约定?

这个约定的主要目的是保证像 HashMap 这样的哈希表数据结构能够正确地工作。在哈希表中,当我们用一个键来查找对应的值时,哈希表会首先根据键的 hashCode() 来决定该键存储在哪个桶中。如果两个对象 ab 的哈希码不同,那么它们会被存储在不同的桶中,查找时不会混淆。

然而,如果两个对象的 hashCode() 相同,它们会被存储在同一个桶中,这时候哈希表会进一步使用 equals() 方法来判断这两个对象是否相等。所以即使它们的哈希码相同,仍然需要通过 equals() 来进行最终的判断。

4. 重写 hashCode() 的注意事项

如果你自定义了一个类,并且重写了 equals() 方法,那么为了保证 HashMap 等哈希表的正确工作,必须同时重写 hashCode() 方法。重写 hashCode() 时,要遵循以下规则:

  1. 一致性:一个对象的 hashCode() 值在对象的生命周期内应该始终保持不变,除非对象的状态发生了变化(即参与 equals() 判断的字段发生了变化)。如果 hashCode() 发生变化,哈希表可能无法正确地定位对象。

  2. 相等对象的哈希码相等:如果两个对象通过 equals() 方法比较相等,那么它们的 hashCode() 必须返回相同的值。

  3. 不相等对象的哈希码可以相同:即使两个对象不相等,它们的 hashCode() 也可以相同。哈希碰撞是可以接受的,只是会稍微影响性能,但不会影响正确性。

3. 为什么重写equals()方法时必须重写hashCode()方法

根据Java的约定,如果两个对象通过equals()方法比较相等,它们的hashCode()方法也必须返回相等的哈希码值。这是为了保证哈希表等数据结构能够正确地工作。例如,如果HashMap中的两个对象相等(通过equals()判断),它们的哈希码也应相等,这样就能确保哈希表能在查找时找到正确的对象。

因此,重写equals()方法时,通常也需要重写hashCode()方法,以保持这两者的一致性。

4. Java中的“128陷阱”

在Java中,Integer类为值在-128到127之间的整数做了缓存,意味着当你创建这些值的Integer对象时,它们会指向同一个对象实例。这是因为Java为了优化性能,缓存了这个范围内的Integer对象。

例如,创建100-128之间的Integer对象时,可能会得到相同的对象引用。这种缓存机制可能会导致一些意外的行为,特别是在使用==进行比较时。

自动装箱与自动拆箱的细节

  • 自动装箱(Autoboxing):Java编译器会自动将基本数据类型转换为相应的包装类对象。例如,将int自动转换为Integerchar自动转换为Character等。

    Integer a = 10; // 自动装箱,将int类型的10转为Integer对象
    
  • 自动拆箱(Unboxing):当需要基本数据类型时,Java编译器会自动将包装类对象转换为相应的基本数据类型。例如,将Integer转换为intDouble转换为double等。

    int b = a; // 自动拆箱,将Integer对象转换为int
    

这种机制虽然非常方便,但也可能带来一些潜在的性能问题,特别是在大量的装箱和拆箱操作中,可能会导致不必要的对象创建。

你可能感兴趣的:(java,开发语言)