蒙在股里 发表于 2015-1-18 11:00:08

了解下JAVA的Java外部类this$0字段发生的一个小bug

不得不提一下的是:.net是看到java红,而开发出来的工具。
起首检察上面一段代码,我指出了成绩代码的地点,读者先本人思索一下这段代码会有甚么成绩。
这是用clone办法完全拷贝一个二项堆(BinomialHeap)布局的代码。二项堆中包括一个外部类BinomialHeapEntry,这个外部类的对象即二项堆中的每个结点,除包括结点对应的关头字外,还纪录父节点parent,下一个兄弟结点sibling和第一个孩子结点child三个指针。二项堆的根表经由过程每棵二项树根节点的sibling指针链接。
cloneBinomialTree(BinomialHeapEntryroot)办法递回拷贝一个根节点(含根节点)下的全部二项树,clone办法中遍历根表中每一个树根结点,拷贝对应的二项树,然后将新的head指针赋给拷贝后的新堆。

publicclassBinomialHeap<E>implementsCloneable{
transientBinomialHeapEntryhead;//根表中的第一个元素
intsize;//全部二项堆的巨细(结点数)

privateclassBinomialHeapEntry{
Eelement;
transientBinomialHeapEntryparent=null,child=null,
sibling=null;
transientintdegree=0;

privateBinomialHeapEntry(Eelement){
this.element=element;
}

//取得孩子结点的迭代器
privateIterable<BinomialHeapEntry>children{...}
}

@Override
publicBinomialHeap<E>clone(){
//TODOAuto-generatedmethodstub
BinomialHeap<E>clone;
try{
clone=(BinomialHeap<E>)super.clone();
}catch(CloneNotSupportedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
thrownewInternalError();
}

BinomialHeapEntrynewHead=null,curRoot=null;
//遍历根表
Iterator<HeapEntry<E>>rootIt=this.roots().iterator();
while(rootIt.hasNext()){
BinomialHeapEntryroot=(BinomialHeapEntry)rootIt.next();
//拷贝根节点下的整棵二项树<br><spanstyle="color:#ff0000;">//BUG引进的中央</span>
BinomialHeapEntrynewRoot=cloneBinomialTree(root);
if(curRoot==null){
newHead=newRoot;
}else{
curRoot.sibling=newRoot;
}
curRoot=newRoot;
}

clone.head=newHead;
returnclone;
}

//拷贝根节点(含根节点)下的整棵子树
privateBinomialHeapEntrycloneBinomialTree(BinomialHeapEntryroot){
BinomialHeapEntrynewRoot=newBinomialHeapEntry(root.element());
BinomialHeapEntrycurChild=null;
//遍历根节点的一切孩子结点
Iterator<HeapEntry<E>>childIt=root.children().iterator();
while(childIt.hasNext()){
BinomialHeapEntrychild=(BinomialHeapEntry)childIt.next();
//递回拷贝孩子节点下的子树
BinomialHeapEntrynewChild=cloneBinomialTree(child);
newChild.parent=newRoot;
if(curChild==null){
newRoot.child=newChild;
}else{
curChild.sibling=newChild;
}
curChild=newChild;
}
newRoot.degree=root.degree;
returnnewRoot;
}

}先回忆一下Java外部类的一些常识,非静态的Java外部类作为内部类的一个成员,只能经由过程内部类的对象来会见,要创立一个外部类对象,也必要经由过程内部类对象来创立,即:outerObject.newInnerClass()。这时候,所创立的外部类对象会发生称号为this$0的一个字段,该字段保留的是创立这个外部类对象的内部类对象援用,即方才的outerObject。这个援用使得一个外部类对象能够自在的会见内部类的成员变量。
在回忆下面二项堆拷贝的代码,在挪用cloneBinomialTree办法拷贝二项树时,我们试图经由过程newBinomialHeapEntry()来创立一个新的结点,把新的结点作为新二项堆中的结点,但现实上我们实践挪用的是this.newBinomialHeapEntry(),也就是说创立的新结点中,this$0是指向老的二项堆(this)而不是新的正在拷贝的二项堆(clone)。这使得我们失掉拷贝的新二项堆以后,经由过程新二项堆的任一结点会见和修正全部堆的信息时,修正的都是老的二项堆,最初发生了一系列奇异的了局。
要想修复这个bug很复杂,把clone办法中的BinomialHeapEntrynewRoot=cloneBinomialTree(root)即白色正文下的语句,修正为BinomialHeapEntrynewRoot=clone.cloneBinomialTree(root)。如许cloneBinomialTree办法中创立的结点都是经由过程clone对象创立,成绩也就办理了。
成绩实在对照复杂,这的确是今后在编程中值得注重的一点:拷贝外部类对象时思索分明拷贝后的对象this$0字段是不是指向的是你但愿指向的内部类对象。
检察本栏目更多出色内容:http://www.bianceng.cn/Programming/Java/

C#跟java类似,但是在跨平台方面理论上可以跨平台,实际上应用不大,执行性能优于java,跟C++基本一致,但是启动速度还是慢.代码安全,但容易性能陷阱.

冷月葬花魂 发表于 2015-1-20 12:32:05

在全球云计算和移动互联网的产业环境下,Java更具备了显著优势和广阔前景。

莫相离 发表于 2015-1-29 06:41:04

关于设计模式的资料,还是向大家推荐banq的网站 http://www.jdon.com/,他把GOF的23种模式以通俗易懂的方式诠释出来,纯Java描述,真是经典中的经典。

若天明 发表于 2015-2-5 21:42:47

我大二,Java也只学了一年,觉得还是看thinking in java好,有能力的话看英文原版(中文版翻的不怎么好),还能提高英文文档阅读能力。

兰色精灵 发表于 2015-2-13 15:30:17

还好,SUN提供了Javabean可以把你的JSP中的 Java代码封装起来,便于调用也便于重用。

海妖 发表于 2015-3-3 23:06:40

Java自面世后就非常流行,发展迅速,对C++语言形成了有力冲击。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于个人PC、数据中心、游戏控制台

变相怪杰 发表于 2015-3-11 13:53:07

你就该学一学Servlet了。Servlet就是服务器端小程序,他负责生成发送给客户端的HTML文件。JSP在执行时,也是先转换成Servlet再运行的。虽说JSP理论上可以完全取代Servlet,这也是SUN推出JSP的本意,可是Servlet用来控制流程跳转还是挺方便的,也令程序更清晰。接下来你应该学习一下Javabean了,可能你早就看不管JSP在HTML中嵌Java代码的混乱方式了,这种方式跟ASP又有什么区别呢?

若相依 发表于 2015-3-18 15:47:57

你快去找一份Java的编程工作来做吧(如果是在校学生可以去做兼职啊),在实践中提高自己,那才是最快的。不过你得祈祷在公司里碰到一个高手,而且他 还愿意不厌其烦地教你,这样好象有点难哦!还有一个办法就是读开放源码的程序了。我们知道开放源码大都出自高手,他们设计合理,考虑周到,再加上有广大的程序员参与,代码的价值自然是字字珠叽,铿锵有力(对不起,偶最近《金装四大才子》看多了)。

小妖女 发表于 2015-3-26 02:39:01

我大二,Java也只学了一年,觉得还是看thinking in java好,有能力的话看英文原版(中文版翻的不怎么好),还能提高英文文档阅读能力。
页: [1]
查看完整版本: 了解下JAVA的Java外部类this$0字段发生的一个小bug