Java内部类this$0字段产生的一个小bug

首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题。

这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码。二项堆中包含一个内部类BinomialHeapEntry,这个内部类的对象即二项堆中的每一个结点,除了包含结点对应的关键字外,还记录父节点parent,下一个兄弟结点sibling和第一个孩子结点child三个指针。二项堆的根表通过每棵二项树根节点的sibling指针链接。

cloneBinomialTree(BinomialHeapEntry root)方法递归拷贝一个根节点(含根节点)下的整个二项树,clone方法中遍历根表中每个树根结点,拷贝对应的二项树,然后将新的head指针赋给拷贝后的新堆。

public class BinomialHeap<E> implements Cloneable {
        transient BinomialHeapEntry head; // 根表中的第一个元素
        int size;                         // 整个二项堆的大小(结点数)

        private class BinomialHeapEntry {
        E element;
        transient BinomialHeapEntry parent = null, child = null,
                sibling = null;
        transient int degree = 0;

                private BinomialHeapEntry(E element) {
            this.element = element;
        }

                // 获得孩子结点的迭代器
                private Iterable<BinomialHeapEntry> children {...}
        }

        @Override
    public BinomialHeap<E> clone() {
        // TODO Auto-generated method stub
        BinomialHeap<E> clone;
        try {
            clone = (BinomialHeap<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new InternalError();
        }

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

        clone.head = newHead;
        return clone;
    }

        // 拷贝根节点(含根节点)下的整棵子树
    private BinomialHeapEntry cloneBinomialTree(BinomialHeapEntry root){
        BinomialHeapEntry newRoot = new BinomialHeapEntry(root.element());
        BinomialHeapEntry curChild = null;
                // 遍历根节点的所有孩子结点
        Iterator<HeapEntry<E>> childIt = root.children().iterator();
        while(childIt.hasNext()){
            BinomialHeapEntry child = (BinomialHeapEntry) childIt.next();
            // 递归拷贝孩子节点下的子树
                        BinomialHeapEntry newChild = cloneBinomialTree(child);
            newChild.parent = newRoot;
            if(curChild == null){
                newRoot.child = newChild;
            } else {
                curChild.sibling = newChild;
            }
            curChild = newChild;
        }
        newRoot.degree = root.degree;
        return newRoot;
    }

}

先回顾一下Java内部类的一些知识,非静态的Java内部类作为外部类的一个成员,只能通过外部类的对象来访问,要创建一个内部类对象,也需要通过外部类对象来创建,即:outerObject.new InnerClass()。这时,所创建的内部类对象会产生名称为this$0的一个字段,该字段保存的是创建这个内部类对象的外部类对象引用,即刚才的outerObject。这个引用使得一个内部类对象可以自由的访问外部类的成员变量。

在回顾上面二项堆拷贝的代码,在调用cloneBinomialTree方法拷贝二项树时,我们试图通过new BinomialHeapEntry()来创建一个新的结点,把新的结点作为新二项堆中的结点,但事实上我们实际调用的是this.new BinomialHeapEntry(),也就是说创建的新结点中,this$0是指向老的二项堆(this)而不是新的正在拷贝的二项堆(clone)。这使得我们得到拷贝的新二项堆之后,通过新二项堆的任一结点访问和修改整个堆的信息时,修改的都是老的二项堆,最后产生了一系列奇怪的结果。

要想修复这个bug很简单,把clone方法中的BinomialHeapEntry newRoot = cloneBinomialTree(root)即红色注释下的语句,修改为BinomialHeapEntry newRoot = clone.cloneBinomialTree(root)。这样cloneBinomialTree方法中创建的结点都是通过clone对象创建,问题也就解决了。

问题其实比较简单,这确实是以后在编程中值得注意的一点:拷贝内部类对象时考虑清楚拷贝后的对象this$0字段是否指向的是你希望指向的外部类对象。

查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/Java/

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索对象
, 拷贝
, 除了root节点
, 结点
, 节点
, 二项堆
, 递归原理java类的问题
, 一个
, 递归拷贝
, 内部
拷贝节点
java 内部类 this、内部类中的this、内部类 this、匿名内部类 this、禅道如何增加bug字段,以便于您获取更多的相关知识。

时间: 2024-10-27 18:14:02

Java内部类this$0字段产生的一个小bug的相关文章

新人学习java。在继承上遇到的一个小问题

问题描述 新人学习java.在继承上遇到的一个小问题 代码如下: package construter; class Game { Game(int i){ System.out.println("Game"); }; } class BoarderGame extends Game{ BoarderGame(int i){ super(i);//当我把super参数中的i替换成一个int型数字时就会报错?? System.out.println("BoarderGame c

c++-二叉树搜索的问题,以及一个小bug

问题描述 二叉树搜索的问题,以及一个小bug #include<iostream> #include<string> using namespace std; class node{ public: string name; string keyword; node* left; node* right; node(string a = "0", string b = "0", node* c = 0, node* d = 0) : name

Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决

原文:Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决 最近一直在开发XX的uwp版本,也是边摸索边做,最近遇到几个比较奇怪的问题,记录于此. 1.项目可用部署到PC,但无法部署到手机,提示以下错误: 错误 : DEP0001 : 意外错误: Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9 为了方便开发,我将常用的类库引用好.默认的几个页面做

menu-关于popupwindow学习中出现的一个小bug

问题描述 关于popupwindow学习中出现的一个小bug 我设置了一个buttonde点击事件 点击后能弹出popuowindow的框 ,我也设置了menu键,点击menu键 也可以弹出这个popupwindow的框 ,但是奇怪的地方在于,如果我不先点击button,我点menu键,它不会再弹出这个popupwindow的框了 ,意思就是只有先点button弹出那个popupwindow框先,点击menu才会有popupwindow这个框.我一直找不到原因,特地找各位大神帮帮忙!然后还有一个

用VC++6.0的控制台实现2048小游戏的程序_C 语言

首先感谢这位大侠的无私分享,仔细学习这个程序以后收获很多,试着添加一些注释 源程序是从开源中国看到的,原作者是 刘地(sir?) 地址为http://www.oschina.net/code/snippet_593413_46040 geek_monkey于2015年3月5日为拜读该程序,受益匪浅 为了方便自己,以及更多初学者阅读,我试着写了写了注释供参考 我是C语言初学者,如有错误希望指正.轻喷 复制代码 代码如下: #include <stdlib.h> #include <stdi

select-mysql 嵌套查询,一个表的多个字段 作为另一个表的条件,若为空输出0,求高手

问题描述 mysql 嵌套查询,一个表的多个字段 作为另一个表的条件,若为空输出0,求高手 SELECT daan FROM 表A a,表B a WHERE daan=b.选项1 or daan =b.选项2 问题来了 若 从表A 查 表B的选项,daan为空,怎么输出0 解决方案 首先得搞明白select daan 就是这一列 为空,输出0属于计数,可以用函数 这两个并不能写一起 解决方案二: 可以使用case when阿 SELECT case when rtrim(daan) ='' th

java中文乱码-java开发前端ftl页面上传一个中文的字段到java后台变成乱码!

问题描述 java开发前端ftl页面上传一个中文的字段到java后台变成乱码! 有没有比在后台写一些转换格式的代码更简单的方式?是不是我哪里的格式没有设置好? 解决方案 找到答案了.原来是我tomcat的编码格式没有设置成utf-8. 解决方案二: ftl文件默认的编码格式是GBK的,但是它输出的编码格式是utf-8的.中文就会显示乱码.在改变ftl文件的编码格式之前,将页面整个copy到txt文档中,然后改变编码格式之后,再将txt文件中的内容复制到原来的文件中. 解决方案三: 客户端 编码格

Java内部类的继承(全)_java

下面通过实例代码给大家分享下有关JAVA内部类的继承,具体详解如下: Java内部类的构造器必须连接到指向其外围类对象的引用(构造内部类必须给它一个外部类对象的引用,内部类依赖于外部类对象),所以在继承内部类的时候,需要在导出类的构造器中手动加入对基类构造器的调用. 因为,在导出类实例化时,并不存在一个外围类对象,以让导出类的实例去连接到它. 所以,我们需要创建一个外围类,然后用一个特定的语法来表明内部类与外围类的关系. 在下例子中,需要给导出类InheritInner一个来自内部类的外围类中的

java内部类-java实验题 内部类的定义与使用

问题描述 java实验题 内部类的定义与使用 定义一个Cube类,其中包含一个变量height,一个方法:计算立方体的体积(带形参,形参为面积).并在其内部定义一个Rectangle类,其中包含两个变量width和height,一个方法:计算底面积. 解决方案 class Cube { private double height; Cube() {} Cube(double height) { this.height = height; } public double volume(double