类A与类B构造器互相依赖对方实例,能否创建非空实例?
如何创建互相持有对方实例的A和B对象
当然可以实现这个需求!这种循环依赖的实例创建在Java里是完全可行的,下面分享几种实用的方案:
方案一:使用Setter方法补全引用
这是最直观也最安全的方式,核心思路是先创建其中一个实例(暂时传null),再创建另一个实例,最后通过Setter方法把引用补全。
首先需要给A和B类添加Setter方法:
class A { private B b; public A(B b) { this.b = b; } // 添加Setter方法 public void setB(B b) { this.b = b; } public B getB() { return b; } } class B { private A a; public B(A a) { this.a = a; } // 添加Setter方法 public void setA(A a) { this.a = a; } public A getA() { return a; } }
然后创建实例的代码:
// 先创建A实例,暂时传入null A a = new A(null); // 创建B实例,传入刚创建的A B b = new B(a); // 给A设置完整的B实例引用 a.setB(b); // 验证:此时a.getB() == b,b.getA() == a,所有实例变量均不为null
这种方式逻辑清晰,不会有对象未完全初始化的风险,推荐在大多数场景下使用。
方案二:利用反射直接赋值实例变量
如果不想修改原类(比如无法添加Setter),可以用反射绕过构造器的限制,直接给实例的私有字段赋值。
代码示例:
import java.lang.reflect.Field; class A { private B b; public A(B b) { this.b = b; } public B getB() { return b; } } class B { private A a; public B(A a) { this.a = a; } public A getA() { return a; } } // 实例创建逻辑 public class Main { public static void main(String[] args) throws Exception { // 先创建两个临时实例,均传入null A a = new A(null); B b = new B(null); // 通过反射获取A类的b字段并赋值 Field bFieldInA = A.class.getDeclaredField("b"); bFieldInA.setAccessible(true); bFieldInA.set(a, b); // 通过反射获取B类的a字段并赋值 Field aFieldInB = B.class.getDeclaredField("a"); aFieldInB.setAccessible(true); aFieldInB.set(b, a); // 验证:此时a.getB()和b.getA()均不为null,且互相引用 } }
注意:反射需要处理NoSuchFieldException、IllegalAccessException等异常,而且设置setAccessible(true)会绕过访问权限检查,在安全限制严格的环境下可能无法使用。
方案三:构造器内部传递未完全初始化的实例
还有一种方式是在其中一个类的构造器里,直接把当前未完全初始化的this传给另一个类的构造器,一次性完成互相引用。
调整类的构造器:
class A { private B b; // 修改A的构造器,内部创建B实例并传入当前this public A() { this.b = new B(this); } public B getB() { return b; } } class B { private A a; public B(A a) { this.a = a; } public A getA() { return a; } }
然后创建实例:
A a = new A(); B b = a.getB(); // 验证:b.getA() == a,a.getB() == b,所有实例变量均不为null
这种方式不需要额外的Setter或反射,但要注意此时A实例在传递给B的构造器时还未完全初始化。如果A的构造器在创建B之后还有其他初始化逻辑,B的实例在这段时间内访问A的其他字段可能会看到未初始化的值,所以要谨慎使用,确保构造器后续逻辑不会影响互相引用的安全性。
内容的提问来源于stack exchange,提问作者M.Dietz
相关产品推荐
相关产品推荐

