- 浏览: 39815 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (17)
- Java (4)
- Spring (0)
- ibatis (1)
- OSGI (0)
- Hibernate (0)
- Oracle (0)
- Linux (1)
- NetWorks (0)
- Javascript (4)
- Groovy (0)
- SOA (0)
- MySQL (2)
- Maven (0)
- JUnit (0)
- My Life (0)
- ExtJS (0)
- XML (0)
- Struts1.x (0)
- Struts2.x (0)
- Security (0)
- Design Pattern (0)
- Data Structure and Algorithm (0)
- C (0)
- C++ (0)
- VB6.0 (1)
- ASP (0)
- .NET (1)
- Perl (0)
- DSL (0)
- Alipay (0)
- Ant (0)
- WebService (0)
- JVM (0)
- DWR (0)
- Ajax (0)
- SSO (0)
- Ofbiz (0)
- EJB (0)
- JQuery (1)
- PHP (1)
- Web (1)
- memcached (1)
最新评论
-
独爱Java:
虽然看不懂,但是强烈希望能进一步学习。留个名。。。
JDK源代码学习系列一---java.util(1) -
liuxuejin:
没有说hashmap里面对hashcode进行hash的函数的 ...
JDK源代码学习系列一---java.util(2) -
llyzq:
呵呵,的确有意思,哪搞的题目
让日本人全部跳下去 -
贫僧不吃肉:
有意思
让日本人全部跳下去 -
chenyong0214:
不可否认写的非常好!
JDK源代码学习系列一---java.util(2)
好吧,我承认我比较懒~ 但是发现不把一些学习成果与工作经验记录下来,我会慢慢将它们遗忘掉,最后一无所有。新年回来,2011从今天开始重新积累吧。
市面上的技术书籍琳琅满目,但哥坚信“有代码有真相”,所以,源代码才是最好的学习材料,先不说Java庞大的开源社区提供的充斥着各种设计模式与创新思路的框架代码,就JDK源代码本身就是一部博大精深的技术圣经。去看看jdk源代码中那些署名的@author...无一不是技术大牛,可以学习他们的代码也许是一件让人激动的事情,这也是开源所带给我们的乐趣。好吧,既然又free,又open,干嘛不去看看呢。
首先看java.util中的HashMap与HashTable吧,这对兄弟在各种java面试题中老是被提及,以前只看过面试题答案中的异同点罗列,但是其内部实现及一些特点却未曾深究。个人觉得看源代码不能像看小说那样毫无目的的从头看下来,可以先给自己准备几个问题,做些猜测,然后再去看实现,这样更有针对性。好吧,哥给本次学习准备了几个给自己的问题。
就先从HashMap开始吧。
新建一个Person类:
现在,同样id的人会被认为是同样的实例...当然,不同id的即使姓名相同也是不同的人,那当把这个Person类的实例作为HashMap的key时,key的唯一性将通过people实例的id
来控制。
打印的结果是
Map m's size :1
key:Person [id=1, name=name1]
value:person2
可见key已存在,value被覆盖,这个结果可以预测。那么接下来我们把代码修改下:
此处的变化是将p1,p2的id设成不同,然后都作为key插入map,因为两个key不相同,所以我们的预测是都可以插入,此时map的size应该为2,待插入后我们修改p2的id为1,即与p1相同,这样就造成了两个entry的key相同的情况,测试再查看map的结构,看看是不是还是刚才插入的两项。
此时我们不知道HashMap的内部实现,所以我们不知道它的实例会不会在数据插入后还继续维持key的唯一性。
我们可以猜测的是三种答案:
1.抛出异常,不允许修改p2的id与p1相同,维护key的唯一性;
2.可以修改,但根据某种算法删除p1或p2中的一项,也能起到维护key的唯一性;
3.可以修改,没有任何事情发生....两项id相同的person实例并存于map中,即存在同一个key对应了两个value。
那么各位在没尝试并且没有查看过HashMap的源代码时会做出怎样的选择呢?
好,我们跑一下程序。
结果打印如下:
Map m's size :2
key:Person [id=1, name=name2]
value:person2
key:Person [id=1, name=name1]
value:person1
那么是预测的第三种情况...这原本不是我最看好的答案..这样我就有一个疑问了,既然可以有两个相同的key对应不同的value存在,那么我通过这个key应该拿到的value是哪个呢?在上述代码的main方法末尾加入以下两行代码:
得到的结果如下:
Map m 通过get方法用key p1:Person [id=1, name=name1]时,获取的value:person1
Map m 通过get方法用key p2:Person [id=1, name=name2]时,获取的value:person1
可见不论你使用p1还是p2,得到的value都是person1。
好吧,现象就先写到这里,在下一篇,我们去边看源代码,边研究这个问题。
下一篇 JDK源代码学习系列一---java.util(2):http://www.iteye.com/topic/907293
原本的目的就是让大家讨论的,技术问题方面我们对事不对人,该争论的还是得争论,这是一种学习态度。
首先您消消火,我回复也是看您很认真对待这个问题,如果不回复可能您还在生闷气。下面表达一下我的意见,您要是不同意也没关系,我们保留意见就好了,何必非要意见一致呢,没准某个特定场合就发生了您说的情况,那您的分析比我的有用多了。
首先逻辑上讲,如果公理都不承认的话讨论定理还有意义吗?太极生两仪,四象演八卦。道之不存,器又焉附呢?没有1+1=2这个“共识”,又怎么敢大胆的说2+1=3呢
其次,假如我们承认了公理,
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html
根据公理,对于我们的场景,可以分析出两点:
1 hashcode是可以变化的,但是一定要equals所依赖的信息变化才行
2 当equals成立时,必有hashcode相等
回头说我们的场景,
当两个对象Key不相等,并且当两次put发生在同一个bucket中时,第一次table[x] = P1,第二次table[x] = P2, P2->next = P1
当P2的key发生变化,并变为和P1一致时:
a)此变化满足条件1,因此可行
b)根据条件2,此时P2的新的hash值等于之前P1的hash值,同时也是P2之前的hash值
c)当根据P1或P2作为key取值时,首先hash值都相等,其次equals成立,第三首先定位到P2,所以返回P2
脱离了general contract再谈就没有意义了,或者我们讨论的不是同一个问题了
这位同学,下下来的jdk里有源代码的压缩包,名字叫src.zip,可以解压看也可以用eclipse attach进去看,推荐后者。注意,不是jdk文档,你的文档可能是jdk用javadoc 生成的文档。
以前还没发现呀!谢谢了!
市面上的技术书籍琳琅满目,但哥坚信“有代码有真相”,所以,源代码才是最好的学习材料,先不说Java庞大的开源社区提供的充斥着各种设计模式与创新思路的框架代码,就JDK源代码本身就是一部博大精深的技术圣经。去看看jdk源代码中那些署名的@author...无一不是技术大牛,可以学习他们的代码也许是一件让人激动的事情,这也是开源所带给我们的乐趣。好吧,既然又free,又open,干嘛不去看看呢。
首先看java.util中的HashMap与HashTable吧,这对兄弟在各种java面试题中老是被提及,以前只看过面试题答案中的异同点罗列,但是其内部实现及一些特点却未曾深究。个人觉得看源代码不能像看小说那样毫无目的的从头看下来,可以先给自己准备几个问题,做些猜测,然后再去看实现,这样更有针对性。好吧,哥给本次学习准备了几个给自己的问题。
就先从HashMap开始吧。
新建一个Person类:
package com.emsn.crazyjdk.java.util; /** * “人”类,重写了equals和hashcode方法...,以id来区分不同的人,你懂的... * * @author emsn1026 * */ public class Person { /** * 身份id */ private String id; /** * 姓名 */ private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } }
现在,同样id的人会被认为是同样的实例...当然,不同id的即使姓名相同也是不同的人,那当把这个Person类的实例作为HashMap的key时,key的唯一性将通过people实例的id
来控制。
package com.emsn.crazyjdk.java.util; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.emsn.crazyjdk.java.util.Person; /** * @author emsn1026 * */ public class MapTest { /** * @param args */ public static void main(String[] args) { Map m = new HashMap(); Person p1 = new Person(); Person p2 = new Person(); p1.setId("1"); p1.setName("name1"); p2.setId("1"); p2.setName("name2"); m.put(p1, "person1"); m.put(p2, "person2"); System.out.println("Map m's size :" + m.size()); for(Object o :m.entrySet()){ Entry e = (Entry)o; System.out.println("key:"+ e.getKey()); System.out.println("value:"+ e.getValue()); } } }
打印的结果是
Map m's size :1
key:Person [id=1, name=name1]
value:person2
可见key已存在,value被覆盖,这个结果可以预测。那么接下来我们把代码修改下:
package com.emsn.crazyjdk.java.util; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.emsn.crazyjdk.java.util.Person; /** * @author emsn1026 * */ public class MapTest { /** * @param args */ public static void main(String[] args) { Map m = new HashMap(); Person p1 = new Person(); Person p2 = new Person(); p1.setId("1"); p1.setName("name1"); p2.setId("2"); p2.setName("name2"); m.put(p1, "person1"); m.put(p2, "person2"); System.out.println("Map m's size :" + m.size()); p2.setId("1"); System.out.println("Map m's size :" + m.size()); for(Object o :m.entrySet()){ Entry e = (Entry)o; System.out.println("key:"+ e.getKey()); System.out.println("value:"+ e.getValue()); } } }
此处的变化是将p1,p2的id设成不同,然后都作为key插入map,因为两个key不相同,所以我们的预测是都可以插入,此时map的size应该为2,待插入后我们修改p2的id为1,即与p1相同,这样就造成了两个entry的key相同的情况,测试再查看map的结构,看看是不是还是刚才插入的两项。
此时我们不知道HashMap的内部实现,所以我们不知道它的实例会不会在数据插入后还继续维持key的唯一性。
我们可以猜测的是三种答案:
1.抛出异常,不允许修改p2的id与p1相同,维护key的唯一性;
2.可以修改,但根据某种算法删除p1或p2中的一项,也能起到维护key的唯一性;
3.可以修改,没有任何事情发生....两项id相同的person实例并存于map中,即存在同一个key对应了两个value。
那么各位在没尝试并且没有查看过HashMap的源代码时会做出怎样的选择呢?
好,我们跑一下程序。
结果打印如下:
Map m's size :2
key:Person [id=1, name=name2]
value:person2
key:Person [id=1, name=name1]
value:person1
那么是预测的第三种情况...这原本不是我最看好的答案..这样我就有一个疑问了,既然可以有两个相同的key对应不同的value存在,那么我通过这个key应该拿到的value是哪个呢?在上述代码的main方法末尾加入以下两行代码:
... System.out.println("Map m 通过get方法用key p1:"+p1+"时,获取的value:"+m.get(p1)); System.out.println("Map m 通过get方法用key p2:"+p2+"时,获取的value:"+m.get(p2)); ...
得到的结果如下:
Map m 通过get方法用key p1:Person [id=1, name=name1]时,获取的value:person1
Map m 通过get方法用key p2:Person [id=1, name=name2]时,获取的value:person1
可见不论你使用p1还是p2,得到的value都是person1。
好吧,现象就先写到这里,在下一篇,我们去边看源代码,边研究这个问题。
下一篇 JDK源代码学习系列一---java.util(2):http://www.iteye.com/topic/907293
评论
37 楼
独爱Java
2011-04-14
虽然看不懂,但是强烈希望能进一步学习。留个名。。。
36 楼
emsn
2011-02-17
superobin 写道
To:yangyi
可能之前说的有点冲,但是没有恶意,你也是个对技术问题很执著的人,我对之前的鲁莽表示歉意~
经过你的举例我发现我之前的结论确实有问题,在极端的情况下,它确实可以取出来。后来经过实例测试也验证了这一点。并不是永远都取不出来。
另外,我认為对於各种问题,尤其是像这种有明确代码的一是一二是二的问题,应该没啥周旋的餘地,这种问题如果有分歧,两个个人肯定有一个错的,如果找不到错在哪,至少有一个人要坚持著错误的观点,这样不好,不对吗?
今天坚持了错误的观点很长时间,经过讨论学到了很多——越是这样的讨论学到的越多,思考越深入。比如今天我就知道了hashCode和equals之间的约定,之前只有个模糊的概念现在也终於清晰了。
再另外,佔领楼主的帖子争论了这麼多,再对楼主表现一下歉意吧~呵呵
可能之前说的有点冲,但是没有恶意,你也是个对技术问题很执著的人,我对之前的鲁莽表示歉意~
经过你的举例我发现我之前的结论确实有问题,在极端的情况下,它确实可以取出来。后来经过实例测试也验证了这一点。并不是永远都取不出来。
另外,我认為对於各种问题,尤其是像这种有明确代码的一是一二是二的问题,应该没啥周旋的餘地,这种问题如果有分歧,两个个人肯定有一个错的,如果找不到错在哪,至少有一个人要坚持著错误的观点,这样不好,不对吗?
今天坚持了错误的观点很长时间,经过讨论学到了很多——越是这样的讨论学到的越多,思考越深入。比如今天我就知道了hashCode和equals之间的约定,之前只有个模糊的概念现在也终於清晰了。
再另外,佔领楼主的帖子争论了这麼多,再对楼主表现一下歉意吧~呵呵
原本的目的就是让大家讨论的,技术问题方面我们对事不对人,该争论的还是得争论,这是一种学习态度。
35 楼
zhaolei415
2011-02-17
java中典型的内存泄露问题啊,修改的字段参与了对象的hashcode的计算,造成存储位置变更
34 楼
superobin
2011-02-17
To:yangyi
可能之前说的有点冲,但是没有恶意,你也是个对技术问题很执著的人,我对之前的鲁莽表示歉意~
经过你的举例我发现我之前的结论确实有问题,在极端的情况下,它确实可以取出来。后来经过实例测试也验证了这一点。并不是永远都取不出来。
另外,我认為对於各种问题,尤其是像这种有明确代码的一是一二是二的问题,应该没啥周旋的餘地,这种问题如果有分歧,两个个人肯定有一个错的,如果找不到错在哪,至少有一个人要坚持著错误的观点,这样不好,不对吗?
今天坚持了错误的观点很长时间,经过讨论学到了很多——越是这样的讨论学到的越多,思考越深入。比如今天我就知道了hashCode和equals之间的约定,之前只有个模糊的概念现在也终於清晰了。
再另外,佔领楼主的帖子争论了这麼多,再对楼主表现一下歉意吧~呵呵
可能之前说的有点冲,但是没有恶意,你也是个对技术问题很执著的人,我对之前的鲁莽表示歉意~
经过你的举例我发现我之前的结论确实有问题,在极端的情况下,它确实可以取出来。后来经过实例测试也验证了这一点。并不是永远都取不出来。
另外,我认為对於各种问题,尤其是像这种有明确代码的一是一二是二的问题,应该没啥周旋的餘地,这种问题如果有分歧,两个个人肯定有一个错的,如果找不到错在哪,至少有一个人要坚持著错误的观点,这样不好,不对吗?
今天坚持了错误的观点很长时间,经过讨论学到了很多——越是这样的讨论学到的越多,思考越深入。比如今天我就知道了hashCode和equals之间的约定,之前只有个模糊的概念现在也终於清晰了。
再另外,佔领楼主的帖子争论了这麼多,再对楼主表现一下歉意吧~呵呵
33 楼
emsn
2011-02-16
我觉得yangyi说的情况是存在的。我现在有个疑问,就是HashMap的hash方法计算不同的hashcode返回的hash值有可能相同吗?我想知道hash冲突发生在仅仅是不同的key对应相同的hashcode这种情况,还是不同的hashcode也会生成相同的hash值?
32 楼
yangyi
2011-02-16
superobin 写道
不算跑题吧,楼主谈的这种情况只是我说的这种情况的一种特例,可以从我说这种情况推断出来。我只是不想让 看似有道理但是不算完美 的解释方法(我认为)摆在最后而已。
既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。
如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。
既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。
如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。
首先您消消火,我回复也是看您很认真对待这个问题,如果不回复可能您还在生闷气。下面表达一下我的意见,您要是不同意也没关系,我们保留意见就好了,何必非要意见一致呢,没准某个特定场合就发生了您说的情况,那您的分析比我的有用多了。
首先逻辑上讲,如果公理都不承认的话讨论定理还有意义吗?太极生两仪,四象演八卦。道之不存,器又焉附呢?没有1+1=2这个“共识”,又怎么敢大胆的说2+1=3呢
其次,假如我们承认了公理,
引用
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html
根据公理,对于我们的场景,可以分析出两点:
1 hashcode是可以变化的,但是一定要equals所依赖的信息变化才行
2 当equals成立时,必有hashcode相等
回头说我们的场景,
当两个对象Key不相等,并且当两次put发生在同一个bucket中时,第一次table[x] = P1,第二次table[x] = P2, P2->next = P1
当P2的key发生变化,并变为和P1一致时:
a)此变化满足条件1,因此可行
b)根据条件2,此时P2的新的hash值等于之前P1的hash值,同时也是P2之前的hash值
c)当根据P1或P2作为key取值时,首先hash值都相等,其次equals成立,第三首先定位到P2,所以返回P2
31 楼
skzr.org
2011-02-16
<div class="quote_title">superobin 写道</div>
<div class="quote_div">不算跑题吧,楼主谈的这种情况只是我说的这种情况的一种特例,可以从我说这种情况推断出来。<span style="color: green;">我只是不想让 看似有道理但是不算完美 的解释方法(我认为)摆在最后而已。</span><br><br>既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。<br>如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。<br>
</div>
<p><br><br>对于所有对象都通用的方法<br><br>Object中的方法:equals,hashCode,toString,clone 和 finalize都有明确的通用约定,所以我们需要遵守,因为不少的类都是按照通用约定来工作的<br><br>这里说说的不少的类就包括java.util.*中的Map,List等等</p>
<p> </p>
<p>特别注意一个就是comparate接口,既然equals都判断true那么compare结果也应当为0</p>
<p> </p>
<p>这些通用约定如果未注意很容易让里的程序或设计出现非预期结果的</p>
<div class="quote_div">不算跑题吧,楼主谈的这种情况只是我说的这种情况的一种特例,可以从我说这种情况推断出来。<span style="color: green;">我只是不想让 看似有道理但是不算完美 的解释方法(我认为)摆在最后而已。</span><br><br>既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。<br>如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。<br>
</div>
<p><br><br>对于所有对象都通用的方法<br><br>Object中的方法:equals,hashCode,toString,clone 和 finalize都有明确的通用约定,所以我们需要遵守,因为不少的类都是按照通用约定来工作的<br><br>这里说说的不少的类就包括java.util.*中的Map,List等等</p>
<p> </p>
<p>特别注意一个就是comparate接口,既然equals都判断true那么compare结果也应当为0</p>
<p> </p>
<p>这些通用约定如果未注意很容易让里的程序或设计出现非预期结果的</p>
30 楼
superobin
2011-02-16
不算跑题吧,楼主谈的这种情况只是我说的这种情况的一种特例,可以从我说这种情况推断出来。我只是不想让 看似有道理但是不算完美 的解释方法(我认为)摆在最后而已。
既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。
如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。
既然放进去任何对象只要hashCode改变就拿不出来(不用提equals,得出这个结论和equals一点关系都没),那么楼主的结论和你所解释的原因就都问题(如果我的解释是正确的)。
如果你觉得这个是跑题,一直认为自己解释的是正确的话,烦请指出我的错误在哪,否则我也认为就真没什么必要继续讨论了。
29 楼
xici_magic
2011-02-16
好文章 持续跟进楼主 期待。
28 楼
yangyi
2011-02-16
superobin 写道
To yangyi:
我要表述的和equals貌似没有关系,代码里我不重载equals结果也是一样的。我要表述的意思是
如果一个放入HashMap的对象的hashCode改变了,则一定查找不到了(在判断的前一半就是false了,根本不会用equals比较了吧)。不管你一共放进map多少个对象、什么顺序、其他对象的key是否与改变后的key一样。
故在这个基础上你写的导致内存泄露的原因是有问题的,虽然结论貌似是一致的。
另:“当equals成立时,hashcode必须相等”这个是固定的约定吗?我怎么记得是“hashcode不同,equals一定返回false”这俩意思好像不太一样,是我记错了?
我要表述的和equals貌似没有关系,代码里我不重载equals结果也是一样的。我要表述的意思是
如果一个放入HashMap的对象的hashCode改变了,则一定查找不到了(在判断的前一半就是false了,根本不会用equals比较了吧)。不管你一共放进map多少个对象、什么顺序、其他对象的key是否与改变后的key一样。
故在这个基础上你写的导致内存泄露的原因是有问题的,虽然结论貌似是一致的。
另:“当equals成立时,hashcode必须相等”这个是固定的约定吗?我怎么记得是“hashcode不同,equals一定返回false”这俩意思好像不太一样,是我记错了?
脱离了general contract再谈就没有意义了,或者我们讨论的不是同一个问题了
27 楼
superobin
2011-02-16
To yangyi:
我要表述的和equals貌似没有关系,代码里我不重载equals结果也是一样的。我要表述的意思是
如果一个放入HashMap的对象的hashCode改变了,则一定查找不到了(在判断的前一半就是false了,根本不会用equals比较了吧)。不管你一共放进map多少个对象、什么顺序、其他对象的key是否与改变后的key一样。
故在这个基础上你写的导致内存泄露的原因是有问题的,虽然结论貌似是一致的。
另:“当equals成立时,hashcode必须相等”这个是固定的约定吗?我怎么记得是“hashcode不同,equals一定返回false”这俩意思好像不太一样,是我记错了?
我要表述的和equals貌似没有关系,代码里我不重载equals结果也是一样的。我要表述的意思是
如果一个放入HashMap的对象的hashCode改变了,则一定查找不到了(在判断的前一半就是false了,根本不会用equals比较了吧)。不管你一共放进map多少个对象、什么顺序、其他对象的key是否与改变后的key一样。
故在这个基础上你写的导致内存泄露的原因是有问题的,虽然结论貌似是一致的。
另:“当equals成立时,hashcode必须相等”这个是固定的约定吗?我怎么记得是“hashcode不同,equals一定返回false”这俩意思好像不太一样,是我记错了?
26 楼
yangyi
2011-02-16
To superobin:
当equals成立时,hashcode必须相等,don't break the contract
再看看
当equals成立时,hashcode必须相等,don't break the contract
再看看
25 楼
emsn
2011-02-16
<div class="quote_title">skzr.org 写道</div>
<div class="quote_div">
<div class="quote_title">emsn 写道</div>
<div class="quote_div">
<div class="quote_title">youjianbo_han_87 写道</div>
<div class="quote_div">1. 先鄙视下论坛规则,我好久没上了,竟然要回答什么尿尿问题。<br>2. 贴出 JDK1.5_update_22中 HashMap的 get方法的源码:<br>/**<br> * Returns the value to which the specified key is mapped in this identity<br> * hash map, or <tt>null</tt> if the map contains no mapping for this key.<br> * A return value of <tt>null</tt> does not <i>necessarily</i> indicate<br> * that the map contains no mapping for the key; it is also possible that<br> * the map explicitly maps the key to <tt>null</tt>. The<br> * <tt>containsKey</tt> method may be used to distinguish these two cases.<br> *<br> * @param key the key whose associated value is to be returned.<br> * @return the value to which this map maps the specified key, or<br> * <tt>null</tt> if the map contains no mapping for this key.<br> * @see #put(Object, Object)<br> */<br> public V get(Object key) {<br> if (key == null)<br> return getForNullKey();<br> int hash = hash(key.hashCode());<br> for (Entry<K,V> e = table[indexFor(hash, table.length)];<br> e != null;<br> e = e.next) {<br> Object k;<br> if (e.hash == hash && ((k = e.key) == key || key.equals(k)))<br> return e.value;//--关键在这里。<br> }<br> return null;<br> }<br><br>通过代码可以得知,如果一个Key对应2个Value,看到注释的部分吗? 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.</div>
<br><img src="/images/smiles/icon_arrow.gif" alt=""> 源代码一贴,神秘感就没了,哈哈</div>
<p><br><br>源码就是硬道理,不过[youjianbo_han_87]可能理解错了楼主的意图了,楼主是在put后,修改了key的关键域字段,人为改变了对象的hashCode和equals的行为(相对put时的状态),给我们设计提了个醒哦:key尽量使用状态不可变的类(偶一般都是Long、Integer、String做key)</p>
<p> </p>
<p><br>这是个坏的例子:既然person使用id做为关键域,逻辑上关键域就不应当再修改了,否则程序或代码行为不可预知。</p>
<p><br>呵呵,以前项目中用map做缓存,都是用的String做key,就是看中了String不可变,建议缓存key对象中这样的关键域做成了final,呵呵调用的想是坏都不行(也遇到过和楼主一样的情形,调用者重用了返回的key,搞三搞四,调试了蛮久才发现,后来基本上返回的尽量new或者clone一个对象)<br><br><strong><span style="color: #ff0000; font-size: medium;">map只认put时的key状态,put后对key修改了关键域->改变了hashCode和equals行为,当然再次get时会按照新的hashCode和equals来定位Entry了</span></strong></p>
<p> </p>
<p> </p>
<p> </p>
</div>
<p>很有道理,从设计角度讲这有不合理的地方,此处仅为讨论问题而故而为之,不推荐像我这么做</p>
<div class="quote_div">
<div class="quote_title">emsn 写道</div>
<div class="quote_div">
<div class="quote_title">youjianbo_han_87 写道</div>
<div class="quote_div">1. 先鄙视下论坛规则,我好久没上了,竟然要回答什么尿尿问题。<br>2. 贴出 JDK1.5_update_22中 HashMap的 get方法的源码:<br>/**<br> * Returns the value to which the specified key is mapped in this identity<br> * hash map, or <tt>null</tt> if the map contains no mapping for this key.<br> * A return value of <tt>null</tt> does not <i>necessarily</i> indicate<br> * that the map contains no mapping for the key; it is also possible that<br> * the map explicitly maps the key to <tt>null</tt>. The<br> * <tt>containsKey</tt> method may be used to distinguish these two cases.<br> *<br> * @param key the key whose associated value is to be returned.<br> * @return the value to which this map maps the specified key, or<br> * <tt>null</tt> if the map contains no mapping for this key.<br> * @see #put(Object, Object)<br> */<br> public V get(Object key) {<br> if (key == null)<br> return getForNullKey();<br> int hash = hash(key.hashCode());<br> for (Entry<K,V> e = table[indexFor(hash, table.length)];<br> e != null;<br> e = e.next) {<br> Object k;<br> if (e.hash == hash && ((k = e.key) == key || key.equals(k)))<br> return e.value;//--关键在这里。<br> }<br> return null;<br> }<br><br>通过代码可以得知,如果一个Key对应2个Value,看到注释的部分吗? 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.</div>
<br><img src="/images/smiles/icon_arrow.gif" alt=""> 源代码一贴,神秘感就没了,哈哈</div>
<p><br><br>源码就是硬道理,不过[youjianbo_han_87]可能理解错了楼主的意图了,楼主是在put后,修改了key的关键域字段,人为改变了对象的hashCode和equals的行为(相对put时的状态),给我们设计提了个醒哦:key尽量使用状态不可变的类(偶一般都是Long、Integer、String做key)</p>
<p> </p>
<p><br>这是个坏的例子:既然person使用id做为关键域,逻辑上关键域就不应当再修改了,否则程序或代码行为不可预知。</p>
<p><br>呵呵,以前项目中用map做缓存,都是用的String做key,就是看中了String不可变,建议缓存key对象中这样的关键域做成了final,呵呵调用的想是坏都不行(也遇到过和楼主一样的情形,调用者重用了返回的key,搞三搞四,调试了蛮久才发现,后来基本上返回的尽量new或者clone一个对象)<br><br><strong><span style="color: #ff0000; font-size: medium;">map只认put时的key状态,put后对key修改了关键域->改变了hashCode和equals行为,当然再次get时会按照新的hashCode和equals来定位Entry了</span></strong></p>
<p> </p>
<p> </p>
<p> </p>
</div>
<p>很有道理,从设计角度讲这有不合理的地方,此处仅为讨论问题而故而为之,不推荐像我这么做</p>
24 楼
superobin
2011-02-16
楼上(yangyi)说的有点问题。最开始我和你的分析一样,而且连容积率、概率等都算进去了,但是后来发现了一个细节彻底推翻了我的推论。
我们先不讨论2个对象的情况,单独对该对象在hashMap中改变hashCode是否可以被查找进行分析:
如你所说
而在get方法内有这样一句:
即便
如果hash改变过,e.hash == hash永远不成立(认为改变hashcode方法以后hash一定不一样)
故,无论如何,只要改过hashCode,肯定查找不到。
借用你结论用一下,我做一点修改就应该是比较正确的答案了:
第一部分没变:
第二部分我修改一下:
假设hash算法不好,两个对象落在同一个bucket上,由于hashCode改变了,源代码写死了不允许hashCode不同的对象被查找,故同样查找不到,存在潜在的内存泄露风险。
结论:如果一个对象的hashcode改变了,则一定查找不到了。
下面附上测试代码:
PS:我把 equals 去掉了,结果是一样的
我们先不讨论2个对象的情况,单独对该对象在hashMap中改变hashCode是否可以被查找进行分析:
如你所说
引用
final int hash; <--Entry的hash永不会变
而在get方法内有这样一句:
int hash = hash(key.hashCode()); //此处省略若干 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value;
即便
引用
假设hash算法不好,两个对象落在同一个bucket上
如果hash改变过,e.hash == hash永远不成立(认为改变hashcode方法以后hash一定不一样)
故,无论如何,只要改过hashCode,肯定查找不到。
借用你结论用一下,我做一点修改就应该是比较正确的答案了:
第一部分没变:
引用
之前当key分别为1和2时,
假设hashcode在优化后得到的table数组下标(这个下标是根据数据power of 2的length取低位得到的[我插一句,这个地方说复杂了,CAPACITY(散列表长度)一定是2的若干次方,那么这个地方是用hashCode对散列表长度取余以确定落在哪个bucket上])不同,则分布在table的两个不同bucket上,当2变成1以后,Person2的对象hash值发生了变化,但是entry的hash值不会发生变化,数组位置也不会变,因为不存在listener。当进行get操作时,首先数组下标根据hash值会定位在1上,而2则无法被找到,只能通过数组遍历取得,存在潜在的内存泄露风险。
假设hashcode在优化后得到的table数组下标(这个下标是根据数据power of 2的length取低位得到的[我插一句,这个地方说复杂了,CAPACITY(散列表长度)一定是2的若干次方,那么这个地方是用hashCode对散列表长度取余以确定落在哪个bucket上])不同,则分布在table的两个不同bucket上,当2变成1以后,Person2的对象hash值发生了变化,但是entry的hash值不会发生变化,数组位置也不会变,因为不存在listener。当进行get操作时,首先数组下标根据hash值会定位在1上,而2则无法被找到,只能通过数组遍历取得,存在潜在的内存泄露风险。
第二部分我修改一下:
假设hash算法不好,两个对象落在同一个bucket上,由于hashCode改变了,源代码写死了不允许hashCode不同的对象被查找,故同样查找不到,存在潜在的内存泄露风险。
结论:如果一个对象的hashcode改变了,则一定查找不到了。
下面附上测试代码:
import java.util.HashMap; import java.util.Map; public class TestMap { public static void main(String[] args) { TestBean bean1 = new TestBean(); bean1.hashCode = 1; Map m = new HashMap(); m.put(bean1, bean1); int j =0,total = 20000;//CAPACITY初始为16,20000个key肯定有落在同一个bucket上的。 for(int i=0;i<total;i++) { bean1.hashCode = i; if(m.get(bean1)==bean1) { j++; } } System.out.println(j); System.out.println((float)j/total); } } class TestBean { static int count = 0; public int hashCode = 0; public int hashCode() { return hashCode; } public String toString() { return "TestBean"+count+"\r\n"; } }
PS:我把 equals 去掉了,结果是一样的
23 楼
yangyi
2011-02-15
是个好问题,不过前面分析的都不太正确,我来试着说明一下。
先看代码:
问题原因:
之前当key分别为1和2时,
假设hashcode在优化后得到的table数组下标(这个下标是根据数据power of 2的length取低位得到的)不同,则分布在table的两个不同bucket上,当2变成1以后,Person2的对象hash值发生了变化,但是entry的hash值不会发生变化,数组位置也不会变,因为不存在listener。当进行get操作时,首先数组下标根据hash值会定位在1上,而2则无法被找到,只能通过数组遍历取得,存在潜在的内存泄露风险。
假设hash算法不好,两个对象落在同一个bucket上,则根据指针变化可知p2在p1之前被定位到,此时将一直返回p2,而p1则是潜在的内存泄露
先看代码:
//section 0 Entry的定义 final K key; V value; Entry<K,V> next; final int hash; <--Entry的hash永不会变 //section 1 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { <--用entry的hash在比较 V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); <--在hash位置的链表中找不到entry时,添加 //section 2 void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); <-- 传入链表head if (size++ >= threshold) resize(2 * table.length); } //section 3 Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; <-- 把当前entry设为head,把原head设为head->next key = k; hash = h; }
问题原因:
之前当key分别为1和2时,
假设hashcode在优化后得到的table数组下标(这个下标是根据数据power of 2的length取低位得到的)不同,则分布在table的两个不同bucket上,当2变成1以后,Person2的对象hash值发生了变化,但是entry的hash值不会发生变化,数组位置也不会变,因为不存在listener。当进行get操作时,首先数组下标根据hash值会定位在1上,而2则无法被找到,只能通过数组遍历取得,存在潜在的内存泄露风险。
假设hash算法不好,两个对象落在同一个bucket上,则根据指针变化可知p2在p1之前被定位到,此时将一直返回p2,而p1则是潜在的内存泄露
22 楼
shhbobby
2011-02-15
很好,我也要一起过一遍
21 楼
elliotann
2011-02-15
哎,JDK编码格式可读性还真差
20 楼
superobin
2011-02-15
改hashCode以后,在map中就找不到这个对象了。具体可参见源码,刚才理解错了,,源码中有个hash == key.hash这种情况的判断,旧的hash是会被存储的,改了hashCode新hash和旧hash应该不会一致吧。。。
19 楼
yangleilt
2011-02-15
emsn 写道
yangleilt 写道
源码在哪里看呀!!我有jdk文档,但是看到的都是相当于uml图的那种解释!没有源码呀??
这位同学,下下来的jdk里有源代码的压缩包,名字叫src.zip,可以解压看也可以用eclipse attach进去看,推荐后者。注意,不是jdk文档,你的文档可能是jdk用javadoc 生成的文档。
以前还没发现呀!谢谢了!
18 楼
youjianbo_han_87
2011-02-15
<div class="quote_title">skzr.org 写道</div>
<div class="quote_div">
<div class="quote_title">emsn 写道</div>
<div class="quote_div">
<div class="quote_title">youjianbo_han_87 写道</div>
<div class="quote_div">1. 先鄙视下论坛规则,我好久没上了,竟然要回答什么尿尿问题。<br>2. 贴出 JDK1.5_update_22中 HashMap的 get方法的源码:<br>/**<br> * Returns the value to which the specified key is mapped in this identity<br> * hash map, or <tt>null</tt> if the map contains no mapping for this key.<br> * A return value of <tt>null</tt> does not <i>necessarily</i> indicate<br> * that the map contains no mapping for the key; it is also possible that<br> * the map explicitly maps the key to <tt>null</tt>. The<br> * <tt>containsKey</tt> method may be used to distinguish these two cases.<br> *<br> * @param key the key whose associated value is to be returned.<br> * @return the value to which this map maps the specified key, or<br> * <tt>null</tt> if the map contains no mapping for this key.<br> * @see #put(Object, Object)<br> */<br> public V get(Object key) {<br> if (key == null)<br> return getForNullKey();<br> int hash = hash(key.hashCode());<br> for (Entry<K,V> e = table[indexFor(hash, table.length)];<br> e != null;<br> e = e.next) {<br> Object k;<br> if (e.hash == hash && ((k = e.key) == key || key.equals(k)))<br> return e.value;//--关键在这里。<br> }<br> return null;<br> }<br><br>通过代码可以得知,如果一个Key对应2个Value,看到注释的部分吗? 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.</div>
<br><img src="/images/smiles/icon_arrow.gif" alt=""> 源代码一贴,神秘感就没了,哈哈</div>
<p><br><br>源码就是硬道理,不过[youjianbo_han_87]可能理解错了楼主的意图了,楼主是在put后,修改了key的关键域字段,人为改变了对象的hashCode和equals的行为(相对put时的状态),给我们设计提了个醒哦:key尽量使用状态不可变的类(偶一般都是Long、Integer、String做key)</p>
<p> </p>
<p><br>这是个坏的例子:既然person使用id做为关键域,逻辑上关键域就不应当再修改了,否则程序或代码行为不可预知。</p>
<p><br>呵呵,以前项目中用map做缓存,都是用的String做key,就是看中了String不可变,建议缓存key对象中这样的关键域做成了final,呵呵调用的想是坏都不行(也遇到过和楼主一样的情形,调用者重用了返回的key,搞三搞四,调试了蛮久才发现,后来基本上返回的尽量new或者clone一个对象)<br><br><strong><span style="color: #ff0000; font-size: medium;">map只认put时的key状态,put后对key修改了关键域->改变了hashCode和equals行为,当然再次get时会按照新的hashCode和equals来定位Entry了</span></strong></p>
<p> </p>
</div>
<p>我不知道哥们你看懂了没,我根本就忽略了他Put的过程,我只是用源码说明,如果Map中一个Key对应了2个Value,如例子中的Person1和Person2为何只能取到前者,至于Put,你看看源码不就知道了。。。。。</p>
<div class="quote_div">
<div class="quote_title">emsn 写道</div>
<div class="quote_div">
<div class="quote_title">youjianbo_han_87 写道</div>
<div class="quote_div">1. 先鄙视下论坛规则,我好久没上了,竟然要回答什么尿尿问题。<br>2. 贴出 JDK1.5_update_22中 HashMap的 get方法的源码:<br>/**<br> * Returns the value to which the specified key is mapped in this identity<br> * hash map, or <tt>null</tt> if the map contains no mapping for this key.<br> * A return value of <tt>null</tt> does not <i>necessarily</i> indicate<br> * that the map contains no mapping for the key; it is also possible that<br> * the map explicitly maps the key to <tt>null</tt>. The<br> * <tt>containsKey</tt> method may be used to distinguish these two cases.<br> *<br> * @param key the key whose associated value is to be returned.<br> * @return the value to which this map maps the specified key, or<br> * <tt>null</tt> if the map contains no mapping for this key.<br> * @see #put(Object, Object)<br> */<br> public V get(Object key) {<br> if (key == null)<br> return getForNullKey();<br> int hash = hash(key.hashCode());<br> for (Entry<K,V> e = table[indexFor(hash, table.length)];<br> e != null;<br> e = e.next) {<br> Object k;<br> if (e.hash == hash && ((k = e.key) == key || key.equals(k)))<br> return e.value;//--关键在这里。<br> }<br> return null;<br> }<br><br>通过代码可以得知,如果一个Key对应2个Value,看到注释的部分吗? 他按顺序找到后,直接就 Return a.value了。而不会循环Person2.</div>
<br><img src="/images/smiles/icon_arrow.gif" alt=""> 源代码一贴,神秘感就没了,哈哈</div>
<p><br><br>源码就是硬道理,不过[youjianbo_han_87]可能理解错了楼主的意图了,楼主是在put后,修改了key的关键域字段,人为改变了对象的hashCode和equals的行为(相对put时的状态),给我们设计提了个醒哦:key尽量使用状态不可变的类(偶一般都是Long、Integer、String做key)</p>
<p> </p>
<p><br>这是个坏的例子:既然person使用id做为关键域,逻辑上关键域就不应当再修改了,否则程序或代码行为不可预知。</p>
<p><br>呵呵,以前项目中用map做缓存,都是用的String做key,就是看中了String不可变,建议缓存key对象中这样的关键域做成了final,呵呵调用的想是坏都不行(也遇到过和楼主一样的情形,调用者重用了返回的key,搞三搞四,调试了蛮久才发现,后来基本上返回的尽量new或者clone一个对象)<br><br><strong><span style="color: #ff0000; font-size: medium;">map只认put时的key状态,put后对key修改了关键域->改变了hashCode和equals行为,当然再次get时会按照新的hashCode和equals来定位Entry了</span></strong></p>
<p> </p>
</div>
<p>我不知道哥们你看懂了没,我根本就忽略了他Put的过程,我只是用源码说明,如果Map中一个Key对应了2个Value,如例子中的Person1和Person2为何只能取到前者,至于Put,你看看源码不就知道了。。。。。</p>
相关推荐
1. **下载压缩包**:从Oracle官网或其他可信源下载`jdk-8u11-linux-x64.tar.gz` 文件到Linux机器上。 2. **解压文件**:使用`tar`命令解压缩文件,例如:`tar -zxvf jdk-8u11-linux-x64.tar.gz`。 3. **移动到安装...
总之,"jdk-8u191-linux-x64.tar.gz"是一个针对64位Linux系统的Java 8更新191版本的安装包,包含了一系列增强的编程特性,如Lambda表达式、Stream API和新的日期时间API,以及Nashorn JavaScript引擎。安装这个JDK...
总之,JDK 8对于Java开发者来说是一个重要的里程碑,它引入了一系列创新特性,提升了开发效率。在Linux环境下,安装和配置JDK 8是一项基础任务,需要理解相关步骤和环境变量的设置。通过正确安装和配置,开发者可以...
- **javac**:Java编译器,将源代码编译成可执行的字节码。 - **java**:JVM的启动器,用于执行`.class`文件。 - **jar**:打包工具,可以将多个类文件打包成一个`.jar`文件。 - **javadoc**:生成API文档的工具...
因此,对于个人和开源项目,推荐使用OpenJDK,它是一个开放源代码的JDK实现,同样支持Java 8,并且在CentOS上安装过程类似。 总的来说,`jdk-8u144-linux-x64.tar.gz`是一个在64位Linux环境下进行Java开发的基础,...
- **javac**: Java编译器,将源代码编译成可执行的字节码(.class文件)。 - **java**: Java解释器,负责运行编译后的Java类文件。 - **jar**: Jar工具,用于创建、修改和提取Java档案(JAR)文件。 - **javadoc**: ...
Java Development Kit(JDK)是Java编程语言的核心组件,它为开发者提供了编译、调试和运行Java应用程序所需的所有工具。"jdk-8u172-linux-x64.tar.gz"是一个针对Linux操作系统的64位版本JDK的压缩包文件。这个文件...
- **javac**:Java编译器,将源代码编译成字节码。 - **java**:Java虚拟机(JVM),用于运行Java应用程序。 - **jar**:Java归档工具,用于打包和管理类库。 - **javadoc**:生成API文档的工具。 - **jps**,**...
1. **下载**:你可以从Oracle官方网站或其他可信源下载"jdk-8u211-linux-x64.tar.gz"。下载完成后,通常会保存在你的`~/Downloads`目录下。 2. **解压**:使用`tar`命令来解压文件。在终端中输入以下命令: ``` ...
Java Development Kit(JDK)是Java编程语言的核心组件,它包含了一组开发工具,用于编写、编译、调试和运行Java应用程序。标题中的"jdk-8u191-linux-x64 .tar.gz"指的是Oracle JDK 8的第191次更新的64位Linux版本,...
1. 使用JDK提供的`javac`编译器将源代码(.java文件)编译成字节码(.class文件)。 2. 使用`java`命令执行已编译的字节码,运行Java应用程序。 3. 使用`jar`工具打包和提取Java应用,创建可执行的JAR文件。 总结,...
使用`javac`命令编译Java源代码,`java`命令运行编译后的字节码。 7. **JDK 8的新特性**: - Lambda表达式:这是一种简洁的函数式编程语法,使得处理集合数据更加高效。 - 方法引用和构造器引用:它们简化了函数...
6. **安装完成后,你可以开始编写Java代码,使用`javac`编译器将源代码编译成字节码,再用`java`命令执行。 JDK 1.8在开发社区中被广泛使用,其丰富的功能和改进极大地提升了Java开发者的效率。了解和熟练掌握这些...
- 使用JDK 1.8开发Java应用,可以通过`javac`命令编译源代码,然后使用`java`命令运行字节码文件。 - 对于开发工具,如Eclipse、IntelliJ IDEA等,需要配置相应的JDK版本以支持JDK 1.8的新特性。 ### 总结 JDK 1.8...
1. **Java编译器** (javac): 用于将源代码转换为字节码,这是Java程序的可执行形式。 2. **Java虚拟机** (JVM): 提供了运行Java程序的环境,它解释并执行字节码。 3. **Java运行时环境** (JRE): 包含了运行Java应用...
9. **开发与调试**: 使用这个JDK版本,开发者可以使用javac编译Java源代码,使用jar打包工具创建可执行的jar文件,使用javadoc生成API文档,以及使用jdb进行调试。同时,JDK还包含了Java虚拟机(JVM),用于执行编译...
Java JDK 1.8是Java开发工具包的一个重要版本,主要针对Windows x64操作系统设计。JDK(Java Development Kit)是开发和运行Java应用程序所必需的软件集合,包括Java编译器、Java运行环境、类库以及各种工具。在这个...
这个版本的JDK包含了Java运行环境(Java Runtime Environment,JRE)和一系列开发工具,如Java编译器(javac)、Java虚拟机(JVM)、Java文档生成器(javadoc)以及性能分析工具等。 1. **JDK的核心组件**: - **...
1. **黑名单正则.java**:这是一个Java源代码文件,可能包含了一个用于处理黑名单的正则表达式逻辑。在Java中,正则表达式可以用来进行字符串匹配、查找、替换等操作,对于数据过滤和验证非常有用。 2. **jdk1.8.0_...
赠送源代码:jetty-util-8.1.8.v20121106-sources.jar; 赠送Maven依赖信息文件:jetty-util-8.1.8.v20121106.pom; 包含翻译后的API文档:jetty-util-8.1.8.v20121106-javadoc-API文档-中文(简体)版.zip; Maven...