首页 文章

java类中的循环依赖

提问于
浏览
28

我有以下课程 .

public class B 
{
    public A a;

    public B()
    {
        a= new A();
        System.out.println("Creating B");
    }
}

public class A 
{
    public B b;

    public A()
    {
        b = new B();
        System.out.println("Creating A");
    }

    public static void main(String[] args) 
    {
        A a = new A();
    }
}

可以清楚地看到,类之间存在循环依赖关系 . 如果我尝试运行A类,我最终得到一个 StackOverflowError .

如果创建了依赖关系图,其中节点是类,则可以轻松识别此依赖关系(至少对于具有少量节点的图) . 那么为什么JVM不能识别这个,至少在运行时? JVM可以在开始执行之前至少发出警告,而不是抛出 StackOverflowError .

[Update] 某些语言不能具有循环依赖关系,因为这样就不会构建源代码 . 例如,see this question和接受的答案 . 如果循环依赖是C#的设计气味那么为什么它不适用于Java呢?只是因为Java可以(编译循环依赖的代码)?

[update2] 最近发现jCarder . 根据该网站,它通过动态检测Java字节代码并在对象图中查找周期来发现潜在的死锁 . 任何人都可以解释该工具如何找到周期?

6 回答

  • 11

    类A的构造函数调用类B的构造函数 . 类B的构造函数调用类A的构造函数 . 您有一个无限递归调用,这就是为什么你最终得到 StackOverflowError .

    Java支持在类之间具有循环依赖关系,此处的问题仅与构造函数相互调用有关 .

    您可以尝试使用以下内容:

    A a = new A();
    B b = new B();
    
    a.setB(b);
    b.setA(a);
    
  • 0

    它在Java中完全有效,在2个类之间有一个循环关系(虽然可以询问有关设计的问题),但是在你的情况下你有不同的动作,每个实例在它的构造函数中创建另一个的实例(这是实际的StackOverflowError的原因) .

    这种特殊模式是已知的相互递归,其中有2个方法A和B(构造函数大多只是方法的一个特例)和A调用B和B调用A.检测这两个方法之间关系的无限循环是可能在琐碎的情况下(你提供的那个),但解决一般情况类似于解决停止问题 . 鉴于解决停止问题是不可能的,因此即使对于简单的情况,编制者通常也不会费心 .

    有可能使用FindBugs模式覆盖一些简单的情况,但对于所有情况都不正确 .

  • 3

    它不一定像你的例子那么容易 . 我相信解决这个问题将等于解决halting problem - 我们都知道 - 这是不可能的 .

  • 14

    如果你真的有这样的用例,你可以按需创建对象(懒惰)并使用getter:

    public class B 
    {
        private A a;
    
        public B()
        {
            System.out.println("Creating B");
        }
    
        public A getA()
        {
          if (a == null)
            a = new A();
    
          return a;
        }
    }
    

    (类似于 A 类) . 因此,只有当您需要时,才会创建必要的对象 . 做:

    a.getB().getA().getB().getA()
    
  • -3

    对依赖项使用组合和构造函数注入的getter / setter的类似解决方法 . 需要注意的重要一点是,对象不会为其他类创建实例,而是传入它们(也就是注入) .

    public interface A {}
    public interface B {}
    
    public class AProxy implements A {
        private A delegate;
    
        public void setDelegate(A a) {
            delegate = a;
        }
    
        // Any implementation methods delegate to 'delegate'
        // public void doStuff() { delegate.doStuff() }
    }
    
    public class AImpl implements A {
        private final B b;
    
        AImpl(B b) {
            this.b = b;
        }
    }
    
    public class BImpl implements B {
        private final A a;
    
        BImpl(A a) {
            this.a = a;
        }
    }
    
    public static void main(String[] args) {
        A proxy = new AProxy();
        B b = new BImpl(proxy);
        A a = new AImpl(b);
        proxy.setDelegate(a);
    }
    
  • 24

    请看http://java.dzone.com/articles/tackling-circular-dependency上的文章

    我想它会清除你的怀疑......

相关问题