hash code、equals和“==”三者的关系

本文详细解析了Java中基本变量与对象变量的比较方式,包括==操作符和equals方法的区别,并深入探讨了hashcode的重要性及其在hashmap等集合类中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 1.如果是基本变量,没有hashcode和equals方法,基本变量的比较方式就只有==;

2.如果是变量,由于在java中所有变量定义都是一个指向实际存储的一个句柄(你可以理解为c++中的指针),在这里==是比较句柄的地址(你可以理解为指针的存储地址),而不是句柄指向的实际内存中的内容,如果要比较实际内存中的内容,那就要用equals方法,但是!!!

如果是你自己定义的一个类,比较自定义类用equals和==是一样的,都是比较句柄地址,因为自定义的类是继承于object,而object中的equals就是用==来实现的,你可以看源码。

那为什么我们用的String等等类型equals是比较实际内容呢,是因为String等常用类已经重写了object中的equals方法,让equals来比较实际内容,你也可以看源码。

3. hashcode
在一般的应用中你不需要了解hashcode的用法,但当你用到hashmap,hashset等集合类时要注意下hashcode。

你想通过一个object的key来拿hashmap的value,hashmap的工作方法是,通过你传入的object的hashcode在内存中找地址,当找到这个地址后再通过equals方法来比较这个地址中的内容是否和你原来放进去的一样,一样就取出value。

所以这里要匹配2部分,hashcode和equals
但假如说你new一个object作为key去拿value是永远得不到结果的,因为每次new一个object,这个object的hashcode是永远不同的,所以我们要重写hashcode,你可以令你的hashcode是object中的一个恒量,这样永远可以通过你的object的hashcode来找到key的地址,然后你要重写你的equals方法,使内存中的内容也相等。。。

 

 

 

 

HashCode就是一个散列码。一般情况下,如果hashCode相同,则equals应该也判定相等。就像MD5一样,但没MD5那么复杂。

散列的价值在于速度,使得查询得以快速进行。

查询一个值的过程首先就是计算散列码,然后使用散列码查询数组。数组并不直接保存值,而是保存值的list。然后对list中的值使用equels()方法进行线性查询。这部分查询会比较慢,但是,如果散列函数好的话,数组的每个位置只有较少的值。因此不是查询真个list,而是快速的跳到数组的某个位置,只对少数的元素进行比较。

### Java HashMap `readObject` 方法的反序列化执行链及其相关安全问题 #### 1. **HashMap 的反序列化机制** 在 Java 中,`HashMap` 是一种常用的集合类。为了支持对象的持久化存储恢复功能,`HashMap` 实现了 `Serializable` 接口,并定义了一个私有的 `readObject` 方法用于自定义反序列化逻辑。 当一个 `HashMap` 被反序列化时,其内部结构会被重新构建。具体来说,`readObject` 方法会读取输入流中的键值对数据并将其填充到新的哈希表实例中[^1]。此过程可能涉及多个方法调用,例如 `put` `hash`,这些方法可能会间接触发其他操作。 以下是简化版的 `readObject` 方法实现: ```java private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // 默认的字段初始化 s.defaultReadObject(); int size = s.readInt(); // 获取条目数量 if (size >= 0) { for (int i = 0; i < size; i++) { K key = (K) s.readObject(); // 读取键 V value = (V) s.readObject(); // 读取值 putVal(hash(key), key, value, false, true); // 插入键值对 } } else { throw new InvalidObjectException("Invalid entry count"); } } ``` 在此过程中,`hash(key)` 方法被调用来计算键的散列码,而 `putVal(...)` 则负责实际插入操作。如果键实现了特定的行为(如重写了 `hashCode()` 或者关联了复杂的逻辑),则可能导致额外的操作被执行。 --- #### 2. **URL 类的 `hashCode` 方法行为** 根据引用描述,在某些情况下,`URL` 对象的 `hashCode` 值初始设为 `-1`,这意味着第一次调用 `hashCode()` 时会委托给底层的 `URLStreamHandler` 来动态计算散列值。这种设计虽然灵活,但也引入了一定的风险——恶意构造的对象可以通过覆盖默认行为来控制散列函数的结果。 假设攻击者能够提供一个伪造的 `URL` 对象作为 `HashMap` 键的一部分,则每次访问该键都会触发动态散列计算逻辑。如果这一逻辑本身存在漏洞或者可以被滥用,则可能成为潜在的安全隐患。 --- #### 3. **Gadget 链的概念与应用** 所谓 Gadget 链是指一系列精心挑选的方法调用路径,它们通过串联起来形成某种预期之外的效果。在 Java 反序列化场景下,常见的目标是利用已知库中的合法代码片段组合成远程命令执行(Remote Code Execution, RCE)或其他破坏性后果。 以 JDK 自身为例,可能存在如下典型的 gadget 组合: - 使用 `TemplatesImpl` 动态加载恶意字节码; - 结合 `LazyMap` 或类似的装饰器模式组件创建延迟求值入口点; - 将上述构造嵌套至容器类型(如 `HashSet`, `LinkedHashMap` 等)以便于传播触发条件; 最终形成的 payload 往往依赖于多层封装关系以及各部分之间的交互特性。例如,当某个成员变量更新时自动回调指定处理器,从而激活隐藏的功能模块[^2]^. 特别需要注意的是,即使是在正常业务流程外偶然发生的副作用也可能构成威胁源。比如前述提到的 `map.put(tiedMapEntry, ...)` 场景里因尝试获取不存在项而导致异常中断现象就属于此类情况之一[^5]. --- #### 4. **防范措施建议** 针对以上分析得出的一些改进建议包括但不限于以下几个方面: - **严格校验外部输入**: 对所有来自不可信渠道的数据进行全面验证后再参与运算; - **最小权限原则**: 确保运行环境中仅暴露必要的 API 表面区域减少受攻击面面积; - **白名单策略管理允许使用的类列表**, 并定期审查第三方依赖版本状态确保及时修补已发现问题; 此外还可以考虑采用专门工具扫描项目是否存在易受此类攻击影响的部分代码段落。 --- ### 示例代码展示如何防御不当使用 下面给出一段简单的防护示范代码: ```java public class SafeDeserializer implements ObjectInputValidation { @Override public void validateObject() throws InvalidObjectException { System.out.println("Validating deserialized object..."); // Add custom validation logic here... } private static final long serialVersionUID = 1L; transient Set<String> safeKeys = Collections.synchronizedSet(new HashSet<>()); public void addSafeKey(String key){ this.safeKeys.add(key); } public boolean containsSafeKey(Object obj){ return this.safeKeys.contains(obj.toString()); } } // Usage Example During Deserialization Process Validation Hook Point Implementation. ObjectInputStream ois = new ObjectInputStream(inputStream){ protected Class<?> resolveClass(ObjectStreamClass desc)throws IOException,ClassNotFoundException{ String className=desc.getName(); // Blocklist Approach Or Whitelist As Needed Per Use Case Requirements Etc.. if(!className.startsWith("java.") && !className.equals("com.example.SafeDeserializer")){ throw new SecurityException("Unauthorized attempt to deserialize "+className+" !"); } return super.resolveClass(desc); } }; ois.registerValidation((SafeDeserializer)newInstance(),0); final Object result = ois.readObject(); if(result instanceof Map<?,?> mapResult){ Iterator<?> it = ((Collection<?>)mapResult.values()).iterator(); while(it.hasNext()){ Object valItm =it.next(); if(!(valItm instanceof Serializable)){ throw new IllegalStateException("Non serializable element found inside collection!"); } if(((SafeDeserializer)oos).containsSafeKey(valItm)==false){ log.warn("{} Not marked as pre-approved!",valItm.getClass().getSimpleName()); } } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值