我最近正在阅读一些Java并且遇到了一些新的东西(一个成语?)对我来说很新:在程序中,具有多个构造函数的类也总是包含一个空白构造函数。例如:

public class Genotype {
  private boolean bits[];
  private int rating;
  private int length;
  private Random random;

  public Genotype() {              //  <= THIS is the bandit, this one right here
    random = new Random();
  }

  /* creates a Random genetoype */
  public Genotype(int length, Random r) {
    random = r;
    this.length = length;
    bits = new boolean[length];

    for(int i=0;i<length;i++) {
        bits[i] =random.nextBoolean();
    }
  }

  /* copy constructor */
  public Genotype(Genotype g,Random r) {
    random = r;
    bits = new boolean[g.length];
    rating = g.rating;
    length = g.length;

    for(int i=0;i<length;i++) {
        bits[i] = g.bits[i];
    }

  }
}

第一个构造函数似乎不是“真实的”构造函数。构造函数,似乎在每种情况下都会使用其他构造函数之一。那么为什么要定义构造函数呢?

有帮助吗?

解决方案

我不确定您所阅读的代码是否具有高质量(我过去曾审阅过一些生物信息学代码,但遗憾的是,这些代码通常不是由专业开发人员编写的)。例如,第三个构造函数不是复制构造函数,并且通常在此代码中存在问题,因此我不会“过多地阅读它”。

第一个构造函数是默认构造函数。它只初始化最低限度,并允许用户使用getter和setter设置其余部分。其他构造函数通常是“便利构造函数”。这有助于创建较少调用的对象。但是,这通常会导致构造函数之间的不一致。实际上,最近的研究表明,默认构造函数以及后续调用setter是可取的。

在某些情况下,默认构造函数也很重要。例如,某些框架(如digester(用于直接从XML创建对象))使用默认构造函数。 JavaBeans通常使用默认构造函数等。

此外,某些类继承自其他类。当父对象的初始化“足够好”时,您可能会看到默认构造函数。

在这种特定情况下,如果未定义该构造函数,则必须事先知道所有细节。这并不总是可取的。

最后,一些IDE自动生成一个默认构造函数,编写该类的人可能不敢删除它。

其他提示

对象可序列化

  

为了允许序列化非可序列化类的子类型,子类型可能负责保存和恢复超类型的public,protected和(如果可访问)包字段的状态。只有当它扩展的类具有可访问的no-arg构造函数来初始化类的状态时,子类型才可以承担此责任。如果不是这种情况,则声明类Serializable是错误的。将在运行时检测到错误。

     

在反序列化期间,将使用类的public或protected no-arg构造函数初始化非可序列化类的字段。必须可以对可序列化的子类访问no-arg构造函数。可序列化子类的字段将从流中恢复

是的,我同意“空白”。构造函数不应该总是存在(根据我的经验,初学者经常犯这个错误),尽管有些情况下空白构造函数就足够了。但是,如果空白构造函数违反了构造后正确实例化所有成员的不变量,则不应使用空构造函数。如果构造函数很复杂,最好将构造分成几个protected / private方法。然后根据需要使用静态方法或其他 Factory 类来调用受保护的构造方法。

我上面写的是理想情景。但是,像spring这样的框架会将构造函数逻辑从代码中移除到某些xml配置文件中。您可能具有getter和setter函数,但可能可以从界面中避免,如此处所述

默认构造函数不是必需的。

如果类中没有定义构造函数,那么将自动创建默认(空)构造函数。如果您提供了任何参数化构造函数,那么将不会自动创建默认构造函数,最好自己创建它。在运行时使用依赖项注入和动态代理创建的框架通常需要默认构造函数。所以,这取决于你写的类的用例。

默认构造函数对于功能视图来说是一个很好的实践。 如果对象具有对方法的全局可见性,则使用默认构造函数:例如,您希望在try / catch中记录对象的实际状态 你可以编码

MyObejct myObject=null
try{...
}catch(Exception e){
    log.error(myObject);//maybe print null. information?
}

或者您更喜欢

MyObejct myObject=new Object();
try{...
}catch(Exception e){
log.error(myObject);//sure print  myobject.toString, never null. More information
}

另外,创建一个EMPTY对象没有很多逻辑,但在我看来,实例化一个NULL对象是有害的。 你可以阅读这篇文章

那不是复制构造函数。基本上,在使用某个框架时,您需要空构造函数。应该总是有一个空的构造函数,当然是公共的或私有的,但至少它允许你控制类的实例化(或不实例化)。

我通常会编写一个完全初始化对象的构造函数;如果还有其他人,他们都会使用适当的默认值来调用此(...)。

对象应该100%初始化,并在创建时准备好使用。

一些框架,例如Hibernate,需要一个无参数的构造函数。他们与最佳实践发生冲突的方式让我有时感到不安。

使用默认和空(空白)构造函数会阻止您拥有任何最终字段。这导致了很多可变性,而通常不需要它。

构建器模式允许您混合使用这两种样式,并允许更灵活的初始化,同时通过在工厂后面隐藏多个arg构造函数来保持不变性。

对于某些POJO类或简单类,当您有时希望使用它们对类进行单元测试时,默认构造函数很有用。您不需要模拟它们,您可以使用默认构造函数创建一个对象并测试值集并从中获取它们或将它们作为参数传递。

您想为扩展它的类创建一个空白构造函数 类,因为它已经扩展了类...孩子现在有超级引用它上面的类是它的父级。如果孩子没有指定super(stuff)...内部的东西引用其他构造函数来使用它现在将尝试引用空构造函数。

我不确定错误是什么我现在正在编写我的第一个父对象关系,并在这里查看事情ha haers。

我想我应该添加你创建一个不是空的构造函数的那一刻你失去了默认的空构造函数,所以现在super()在扩展类中是默认的,没有东西要指向。当然如果你创建了扩展类来处理super,通过指定哪个去除默认的super()然后你回避这个,但是如果有人想要使用你的类并从中扩展并且没有意识到没有你可以拥有一套空套 刚刚创建了一个。

这是我的第一篇文章,但是想要了解我的理解。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top