Java实体类 - 为什么需要一个空构造函数?

54

对于您来说,这可能听起来很愚蠢,但为什么我需要在我的 @Entity 中定义一个空构造函数呢?

我看到的每个教程都说:每个实体都需要一个空构造函数。

但是如果您不重新定义,则 Java 会始终为您提供默认的不可见空构造函数。

让我澄清一下。。。“需要”的意思是指需要“编写”。

也就是说,在实体中始终编写一个空构造函数。

例如:

@Entity
public class MyEntity implements Serializable {

   @Id
   private String str;

   public MyEntity(){}

   //here getter and setter
}

但是当你不重新定义(写一个带参数的构造函数)时,Java总是会给你一个空构造函数。

在这种情况下,写这个空构造函数似乎是毫无意义的。


嗯,这并不总是必要的,除非你想为你的数据成员赋特定的值。 - JavaNewbie_M107
1
@JavaNewbie_M107 空构造函数是必须的(是的,它可以是默认构造函数,但仍然必须有没有参数的构造函数)。 - Antoniossss
如果你有一个公共类,在Java中实际上会给你一个公共可见(而不是不可见)的空构造函数。 - steffen
2
@steffen,我所说的“invisible”是指在类声明中它没有出现在我的屏幕上。 - sliders_alpha
@Antoniossss 我认为那不准确。如果您没有提供空构造函数,编译器会自动为任何没有构造函数的类提供一个无参数的默认构造函数。https://docs.oracle.com/javase/tutorial/java/javaOO/constructors.html - Gass
这是我提到的默认值。此外,如果没有明确定义,它并不意味着它不存在。你刚刚确认了我所写的内容。 - Antoniossss
8个回答

59

如果您的持久化框架通过反射来创建新实例,则需要一个空构造函数。如果您没有为该类提供带参数的其他构造函数,则不需要提供空构造函数,因为您会默认得到一个。

您还可以使用@PersistenceConstructor注释,其格式如下:

@PersistenceConstructor
public Movie(Long id) {
    this.id = id;
}

如果您的项目中使用了Spring Data,可以通过初始化实体来避免使用空构造函数。


1
那么框架是不好的,因为它们容易使用Unsafe。 - Enerccio
那么你的意思是说,框架总体来说都不好,因为它们可能会使用Unsafe吗?我不得不反驳。首先,对于构造函数注入,您不需要Unsafe,反射就可以解决问题。其次,有什么替代方案呢?试图自己编写已经由庞大的开发者社区完成的所有工作吗? - u6f6o
我并不是说需要你提供空构造函数的框架是不好的,而是因为如果你不提供空构造函数,它们可以使用 Unsafe - Enerccio
如果我需要多个构造函数怎么办?我需要在所有构造函数上加上@PersistenceConstructor吗? - tam.teixeira
1
不,你只需要为持久化框架定义一个主构造函数。但是你可能会有其他构造函数,但是持久化框架需要知道哪个可用的构造函数应该用于初始化实体,这就是@PersistenceConstructor注释的作用。 - u6f6o
你有任何想法,为什么@PersistenceConstructor注释不起作用吗?我遇到了与此问题相同的异常:https://dev59.com/kq3la4cB1Zd3GeqPVPqw。我想在我的实体中注入DomainEventPublisher,以便我可以发布事件(在这里提出了一些建议https://dev59.com/F7bna4cB1Zd3GeqPfrlP) - Spasoje Petronijević

38
但是Java总是会给你一个默认的不可见的空构造函数(如果您没有重新定义一个)。
只有在您的类中没有提供任何构造函数时,此语句才为真。如果您的类提供了一个参数构造函数,则JVM将不会添加无参构造函数。

4
是的,当时我不知道这个。 - sliders_alpha

7

只有在提供了另一个构造函数时,才需要显式定义默认构造函数。如果提供了另一个构造函数,除了与默认构造函数签名相同的构造函数之外,将不会创建默认构造函数。

由于JPA实现依赖于默认构造函数的存在,因此必须包含将被省略的默认构造函数。


1
我强烈反对,实体必须具有空构造函数以便通过反射进行实例创建。 - Antoniossss
1
你熟悉哪种JPA实现?使用Hibernate时,除非存在上述情况,否则我永远不需要包含默认构造函数。 - Kevin Bowersox
你不必提供没有参数的构造函数,因为你有默认的构造函数 - 所以你确实有一个。我没有写过你必须编写自己的空c,对吧? :) - Antoniossss
@Antoniossss 我认为我们是在一个频道上的。 - Kevin Bowersox

5
由于您指定了“JPA”标签,我假设您的问题仅适用于JPA而不是空构造函数。
持久性框架通常使用反射,更具体地说是Class<T>.newInstance()来实例化对象,然后通过内省调用getter/setter来设置字段。
这就是为什么您需要一个空构造函数和getter/setter的原因。
请参见关于Hibernate中空构造函数的问题的此StackOverflow问题。

3
实际上,您不需要编写它。默认情况下就有。有时,您可以创建一个私有构造函数来防止用户使用默认构造函数。
public class MyClass{

private MyClass(){} 

} 

对于单例模式,例如您可以阻止使用默认构造函数。

有时候,当您使用 Gson 插件将 String Json 数据转换为对象时,它需要编写默认构造函数,否则它无法工作。


1

所有答案都很好。

但是让我们用代码来谈论。以下代码片段将为您提供更清晰的理解。

PersonWithImplicitConstructor.java

public class PersonWithImplicitConstructor {
    
    private int id;
    
    private String name;

}

首先,我们需要编译.java文件。

javac PersonWithImplicitConstructor.java

然后会生成class文件。

在该class文件上运行javap命令,将会得到以下信息。

javap PersonWithImplicitConstructor.class

Compiled from "PersonWithImplicitConstructor.java"
public class PersonWithImplicitConstructor {
  public PersonWithImplicitConstructor();
}

注意:如果您想获取更多信息,可以在javap命令后使用-p标志。

下一个Java文件将只有参数化构造函数。

PersonWithExplicitConstructor.java

public class PersonWithExplicitConstructor {
    
    private int id;
    
    private String name;

    public PersonWithExplicitConstructor(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

使用javac编译PersonWithExplicitConstructor.java文件

使用javap查看PersonWithExplicitConstructor.class文件

Compiled from "PersonWithExplicitConstructor.java"
public class PersonWithExplicitConstructor {
  public PersonWithExplicitConstructor(int, java.lang.String);
}

PersonWithBothConstructors.java

public class PersonWithBothConstructors {

    private int id;
    
    private String name;

    public PersonWithBothConstructors() {

    }

    public PersonWithBothConstructors(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
}

使用javac编译PersonWithBothConstructors.java文件

使用javap查看PersonWithBothConstructors.class文件

Compiled from "PersonWithBothConstructors.java"
public class PersonWithBothConstructors {
  public PersonWithBothConstructors();
  public PersonWithBothConstructors(int, java.lang.String);
}

0

Java并不总是为您的类提供默认的无形空构造函数,如果您的类有参数构造函数,则必须自己定义空构造函数。


-1

从JPA标签来看,我猜测你正在使用Java bean。每个bean都需要具备以下属性:

  1. 为其所有主要实例变量编写getter和setter方法。

  2. 一个空构造函数

  3. 最好将其所有实例变量设置为private

因此,这句话的意思是:"每个实体都需要一个空构造函数"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接