java类中的循环依赖

ath*_*ena 33 java circular-dependency

我有以下课程.

public class B 
{
    public A a;

    public B()
    {
        a= new A();
        System.out.println("Creating B");
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Viv*_*sse 32

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

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

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

A a = new A();
B b = new B();

a.setB(b);
b.setA(a);
Run Code Online (Sandbox Code Playgroud)

  • 但是不是循环依赖不好吗?如果你在代码中实际上有循环依赖(就像你给出的例子),它不是一个糟糕设计的指标吗?如果是,那为什么java支持它呢?如果没有,那么你可以指出一些涉及循环依赖的设计是首选的情况吗? (7认同)
  • 给出一个真实的例子:例如,您可以将`A`重命名为`Parent`,将`B`重命名为`Child`.这种关系在计算中经常发生(`Parent`类需要知道它的子节点是什么,'Child`类需要知道`Parent`是谁),例如当处理XML文档时(子标签包含在父标签中)等等.) (7认同)
  • 在这种情况下,"依赖"意味着什么并不是很清楚.依赖性通常转化为"X"需要*在Y之前发生*. (2认同)
  • 这并没有回答这个问题(关于为什么在运行之前没有通过足够的工具检测到问题)。 (2认同)

Mic*_*ker 17

它在Java中完全有效,可以在2个类之间建立循环关系(尽管可以询问有关设计的问题),但是在您的情况下,您可以使用每个实例的异常操作在其构造函数中创建另一个实例(这是实际的StackOverflowError的原因).

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

有可能使用FindBugs模式覆盖一些简单的情况,但它并不适用于所有情况.


mus*_*iKk 11

它不一定像你的例子那么容易.我相信解决这个问题就等于解决暂停问题 - 正如我们所知 - 这是不可能的.


And*_*ner 5

如果您真的有这样的用例,您可以按需(懒惰地)创建对象并使用 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

(对于班级也类似A)。因此,如果您执行以下操作,则只会创建必要的对象:

a.getB().getA().getB().getA()
Run Code Online (Sandbox Code Playgroud)