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

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

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

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

?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

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: rgb(255, 0, 0);">// 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字段是否指向的是你希望指向的外部类对象。

时间: 2024-10-25 22:07:16

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

解决JSONCPP 0.10.2的一个Bug

最近在使用jsoncpp 0.10.2的过程中碰到一个bug,创建的数组,无法超过5个元素,测试代码如下: int j = 0; int count = 20; Json::Value root; Json::Value item; for (int i = 0; i < count; i++) { root[i] = i; j = root.size(); } 在我的实际项目中,如果数组只有1个是元素(该元素稍微有点大的JSON对象),也有可能出现这个元素的值错误的故障,超过5个肯定出错. 在

NetBSD-1.0 时间设置的一个bug

每次使用 date 命令设置主机时间,再次重启后时间就和设置的不一样了!查看启动时的日志: 对应的代码为 arch/i386/isa/clock.c 的 startrtclock 函数: /* Check diagnostic status */ outb (IO_RTC, RTC_DIAG); if (s = inb (IO_RTC+1)) printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS); 为什么 RTC 的诊断状态会不

织梦添加自定义独立模型缩略图字段官方的一个BUG

dedecms织梦添加自定义独立模型时,官方程序默认会为你添加好这些字段,但是缩略图字段写错了,litpic写成了listpic ,改回来,保存就行了,不然dede:arclistsg无法获取缩略图数据. 按照下图,把listpic改成litpic即可. 原文地址:https://www.cnblogs.com/dedevip/p/9904476.html

java题求代码,4、现在有如下的一个数组: int oldArr[]={1,3,4,5,0,0,6,6,0,5,4,7,6,7,0,5} 要求将以上数组中值为0的项去掉,将不为0的值存入一个新的数组,生成的新数组为: int newArr[]={1,3,4,5,6,6,5,4,7,6,7,5}

public class TEST { public static void main(String[] args) { int [] oldArr= {1,3,4,5,0,0,6,6,0,5,4,7,6,7,0,5}; int [] newArr= new int[oldArr.length] ; int n=0; for (int i=0;i<oldArr.length;i++) { if(oldArr[i]==0) {} else { newArr[n]=oldArr[i]; n++; }

Java内部类

本文是<Java核心技术 卷1>中第六章接口与内部类中关于内部类的阅读总结. Java中的内部类(inner class)是定义在另一个类内部的类.那么内部类有什么用呢?这里主要由三个内部类存在的原因: 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据.即,如果类A中定义了类B,那么类B可以访问类A中的数据,甚至是私有数据,但类A不能访问类B中的私有数据: 内部类可以对同一个包中的其他类隐藏起来.在一个包中,定义一个类时,即使不加上访问权限关键词,这个类也是包内其他类可访问的,不

Java内部类:匿名内部类(四)

Java内部类分为4个部分进行阐述,分别为概览.成员内部类.局部内部类和匿名内部类. 在本文中是Java内部类的匿名内部类,主要讲述匿名内部类的概念.匿名内部类的好处.匿名内部类的使用前提.和匿名内部类的应用场景. 1.匿名内部类的概念 没有类名的类就称作为匿名内部类 2.匿名内部类的好处 简化书写 3.匿名内部类的使用前提 必须存在继承或者实现关系才能使用 4.匿名内部类的应用场景 匿名内部类一般是用于实参 示例代码: package com.rk.innerclass; public cla

java 内部类与外部类的区别

最近在看Java相关知识的时候发现Java中同时存在内部类以及非公有类概念,而且这两个类都可以不需要单独的文件编写,可以与其他类共用一个文件.现根据个人总结将两者的异同点总结如下,如有什么不当地方,欢迎大家指正. 1.非公有类和内部类对应的文件名与这两种类的类名无关: 2.一个源文件中可以包含多个非公有类或者内部类: 3.非公有类不能使用public关键字(一般前面不加关键字),内部类可以使用public.private.protected关键字: 4.非公有类中可以添加0到多个内部类: 5.非

Java内部类详解--成员内部类,局部内部类,匿名内部类,静态内部类

说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本文的目录大纲: 一.内部类基础 二.深入理解内部类 三.内部类的使用场景和好处 四.常见的与内部类相关的笔试面试题 若有不正之处,请多谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3811445.html 一

Java内部类详解

说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本文的目录大纲: 一.内部类基础 二.深入理解内部类 三.内部类的使用场景和好处 四.常见的与内部类相关的笔试面试题 若有不正之处,请多谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3811445.html 一