子类继承父类时父类构造函数自动执行致readDocs重复调用问题
解决PageRankMonteCarlo中readDocs被调用两次的问题
嘿,这个问题我太熟了——Java的继承机制里,父类构造函数的隐式调用确实很容易踩这个坑!你遇到的核心问题是:创建子类对象时,Java会自动调用父类的无参构造函数(除非你显式指定其他父类构造),而你的父类PageRankSparse的构造函数里已经调用了readDocs(),子类构造又手动调用了一次,自然就执行两次了。
下面给你几个实用的解决方案,按优雅程度排序:
方案1:重构父类,分离构造与初始化(推荐)
最佳实践里,构造函数应该只负责对象的基础状态初始化,像readDocs()这种可能涉及IO、耗时操作的方法,应该单独抽出来让调用者控制时机。
修改父类PageRankSparse:
public class PageRankSparse { public PageRankSparse() { // 只做基础变量的初始化,移除readDocs()调用 // 比如初始化空集合、默认参数等 } // 单独的初始化方法 public void readDocs() { // 原来的readDocs逻辑,比如读取文档、构建图结构等 } }
然后子类PageRankMonteCarlo的构造函数只调用一次初始化:
public class PageRankMonteCarlo extends PageRankSparse { public PageRankMonteCarlo() { // 父类无参构造被隐式调用,不会触发readDocs readDocs(); // 仅执行一次初始化 } }
这个方案从根源上避免了重复调用,也让代码职责更清晰,后续维护起来更方便。
方案2:显式调用父类的无副作用构造(如果父类支持)
如果父类有其他不调用readDocs()的构造函数(比如带参数的构造),你可以在子类构造里显式调用它,避开那个触发readDocs()的父类构造:
public class PageRankMonteCarlo extends PageRankSparse { public PageRankMonteCarlo() { // 显式调用父类的某个构造,比如带参数的super(xxx) super(someParameter); // 这个父类构造不会调用readDocs readDocs(); // 自己调用一次初始化 } }
这个方案适合你不能修改父类代码的场景,但前提是父类提供了合适的构造函数。
方案3:给readDocs加幂等性检查(快速补丁)
如果不想改动太多代码,可以给readDocs()加一个标志位,确保即使被多次调用,实际逻辑只执行一次:
修改父类PageRankSparse:
public class PageRankSparse { private boolean docsAlreadyRead = false; // 标志位 public PageRankSparse() { readDocs(); } public void readDocs() { if (docsAlreadyRead) { return; // 已经初始化过,直接返回 } // 原来的readDocs逻辑 // ... docsAlreadyRead = true; // 标记为已初始化 } }
这样不管父类构造还是子类构造调用readDocs(),实际逻辑只会执行一次。这个方案比较快捷,但属于补丁式修复,长期来看还是方案1更优雅。
内容的提问来源于stack exchange,提问作者Sahand
相关产品推荐
相关产品推荐

