我应该在 Java 中对实体和模型使用基元还是对象



作为Java的初学者,我在选择实体或模型类中的变量类型时感到困惑。正如在Java基元与对象中提到的,它们都有一些优点和缺点,例如内存使用。但是,我想知道创建实体或模型类时的一般方法。那么,在这种情况下,我应该为长整型、布尔型、整数等变量选择基元还是对象类型,如下所示?

public class Student {
private Long id;              // long ?
private String name;    
private Boolean isGraduated;  // boolean ?
private Integer age;          // int ?
}

任一工作

如您所指出的,基元需要较少的内存。但是对象具有优势,尤其是在预期对象而不是基元的某些情况下。在某些情况下,可能会使用基元,尽管自动装箱通常会处理这个问题。遇到这些情况时,编译器肯定会提醒您。

Skliar 提出了一个有效的观点,即基元具有默认值,而对象没有。 根据编码情况,两者都可能是一个优势。

请记住,对于您的大部分工作,您不必担心这一点。这种担忧是过早优化的一个例子。你几乎肯定有更重要的事情要担心。

对于许多常见的业务应用,内存使用量无关紧要。我自己的默认设置是使用对象,为了方便和功能。从我多年来的观察来看,用法似乎相当分散,有些人倾向于原始人,有些人倾向于对象。

我对原始对象三思而后行的唯一情况是在处理大量数据的循环时可能会调用自动装箱。在这种情况下,仅仅宣布intInteger可能会通过避免毫无意义的拳击而产生重大影响。但是,同样,不要过分担心这一点,因为依靠自动装箱是完全可以接受的,并且通常是最好的策略。

record

Java 16+ 现在提供记录功能。记录是编写类的简短方法,该类的主要目的是透明且不可变地传达数据。您只需声明成员字段类型和名称。编译器隐式生成构造函数、getter、equals&hashCodetoString方法。

我提到记录只是想说这个方便的新功能可能会变得非常流行,它同时支持对象和基元。因此,使用记录不会影响对象与原始决策。

您的示例类将是:

public record Student( Long id , String name , Boolean isGraduated , Integer age ) {}

。或:

public record Student( long id , String name , boolean isGraduated , int age ) {}

用法与传统类相同。

Student alice = new Student( 101L , "Alice" , false , 33 ) ;

模糊差异

你应该知道,Oracle的Java团队和相关社区对模糊原语和对象之间的区别很感兴趣。当然,诀窍是在尊重极端向后兼容性的同时,这是Java平台的标志。

其中一些工作是作为Valhalla 项目的一部分完成的,包括可能向 Java 添加值类型

要了解有关这方面可能的未来方向的更多信息,请参阅甲骨文的Brian Goetz和其他人的演讲。使用您喜欢的搜索引擎搜索"Java JEP原语"。您将找到JEP的链接,例如:

  • JEP 401:基元对象(预览版)(2021年 5 月5 日更新)
  • JEP 402:将基本基元与对象统一(预览版)(2021年 5 月5 日更新)
  • JEP 218:泛型优于基元类型(2017-10更新)
  • JEP 169:值对象(2019-10更新)

LongInteger变量的默认值为null

但是longint的默认值为0

比较:

如果使用对象:

public class Student {
private Long id;              // long ?
private String name;    
private Boolean isGraduated;  // boolean ?
private Integer age;          // int ?
}
id => null
age => null

如果使用基元:

public class Student {
private long id;              // long ?
private String name;    
private Boolean isGraduated;  // boolean ?
private int age;          // int ?
}
id => 0
age => 0

在许多情况下,将 0 作为默认值会令人困惑,因此在这种情况下使用 Objects 是有意义的。如果对象值为"null",则确定该值未初始化。但是如果原始值是"0",那么不清楚:它是一个默认的未初始化值,还是这个值是用"0"初始化的。

我通常更喜欢使用基元类型,除非我需要显式 null 检查

正如其他人所指出的,您可以使用任一方法。

正如Basil Bourque所指出的那样,如果您使用内存使用量(或某种其他类型的运行时效率度量)作为决定的原因......这很可能是过早的优化。 差异可能太小而无关紧要。


那你应该如何决定呢?

这取决于你。 但>>我的<<规则是:

  • 如果建模告诉你你需要能够表示"此字段未设置"(或类似),那么你可以1使用(说)Integer而不是int......并考虑使用null来表示"未设置"的情况。

  • 当您需要将类型用作泛型参数时(如List<Integer>中所示),您别无选择,只能使用对象类型。 (在当前版本的 Java 中。

  • 如果您没有使用对象类型的特定原因(例如Integer) 使用基元类型(例如int)。 换句话说,这是我的默认选择。


为什么>>my<<默认选择使用基元类型?

因为这样的事情:

private Integer field;  // defaults to null which may lead to an NPE

Integer a = ...
Integer b = ...
if (a == b) // MISTAKE
if (a < b)  // Compilation error

换句话说,基元包装器类型的某些方面比它们的基元类型等效项更容易受到程序员错误的影响。


1 - 另一方面,如果你支持"not set",它会让你的代码更加复杂。 当您使用可为 null 的类型执行此操作时,您必须记住测试null并在使用(裸)字段的任何地方适当地处理它。 除非对此功能有明确的业务需求,否则最好将系统设计为"未设置"字段是不可能的。

我认为没有理由在您的情况下使用对象,但也不认为使用它们会极大地影响性能。就个人而言,如果我没有使用对象的特定原因(例如需要ArrayList<Integer>或任何其他泛型集合),我会使用原语。

再说一次,如果我确实需要大量的整数集合,我会使用数组(例如int[])。

编辑:评论中有人问为什么人们更喜欢List<T>而不是传统的数组。ArrayList<T>等集合是可变的,易于使用。比较将元素添加到ArrayList<T>

List<MyClass> collection = new ArrayList<MyClass>();
collection.add(new MyClass());

。将元素添加到数组:

public static int[] addElement(int sourceArr[], int e)
{
int newArr[] = new int[sourceArr.length + 1];
for (int i = 0; i < sourceArr.length; i++)
{
newArr[i] = sourceArr[i];
}
newArr[newArr.length - 1] = e;
return newArr;
}
// ...
int arr[] = { 1, 2, 3 };
arr = addElement(arr, 4); // { 1, 2, 3, 4 }

最新更新