我应该在Java中使用原始类型还是对象类型来表示实体和模型?

7
作为Java的初学者,我在选择实体或模型类中的变量类型时感到困惑。正如在Java原始类型与对象中提到的那样,它们都有一些优点和缺点,例如内存使用情况。然而,我想知道在创建实体或模型类时的一般方法。因此,在这种情况下,对于长类型、布尔类型、整数类型等变量,我应该选择原始类型还是对象类型,如下所示?
public class Student {
    private Long id;              // long ?
    private String name;    
    private Boolean isGraduated;  // boolean ?
    private Integer age;          // int ?
}


6
您需要考虑字段的可空性。如果它可以为空,则应使用Object。 - Lê Hoàng Dững
1
这个问题实际上是基于观点的,可能不适合在这里讨论。然而,我很少看到有人在泛型之外使用装箱基元类型。 - Henry Twist
3
@HenryTwist Stack Overflow 的“基于观点”的政策旨在避免冗长的开放式讨论,这种讨论往往难以得出解决方案。我认为这并不一定适用于这个问题。针对对象和基本类型之间的选择,可以提出具体的技术优缺点作为考虑因素,供任何程序员做出决策。 - Basil Bourque
@LêHoàngDững 非常重要的一点是,我遇到了一个关于“布尔值”的严重问题。非常感谢,我会记住的。 - user15807010
1
这是代码风格的问题。但是,对于字段来说,使用原始类型更为常见的做法。需要注意的一点是,如果您选择使用对象包装器,则最佳实践是使用可空注释来显式地说明是否接受空引用。对于原始类型,您不必这样做,因为它们不能为null。作为替代方案,可以使用OptionalInt和类似的容器来处理可选值,这些容器具有自我说明性。 - Sokolov
@Sokolov非常有用的评论,补充了其他答案。非常感谢。 - user15807010
4个回答

10

都可以使用

正如您所指出的,原始类型需要更少的内存。但是对象在某些情况下具有优势,特别是在期望使用对象而不是原始类型的情况下。在某些情况下,可能会期望使用原始类型,尽管自动装箱通常会处理这种情况。编译器在遇到这些情况时肯定会警告您。

并且Skliar提出了一个合理的观点,即原始类型具有默认值,而对象没有默认值。根据编码情况,这两者都可能是优势。

请记住,在大多数情况下,您不应该担心这个问题。这样的担忧是过早优化的例子。您几乎可以确定有更重要的事情要担心。

对于许多常见的业务应用程序,内存使用量微不足道。我个人的默认设置是使用对象,因为它们方便并具有各种功能。从我多年来注意到的情况来看,使用情况似乎相当分散,有些人倾向于使用原始类型,有些人倾向于使用对象。

唯一需要思考原始类型与对象的情况是处理大量数据时可能会调用自动装箱的情况。在这种情况下,通过避免无意义的装箱,仅声明int而不是Integer可能会产生显着影响。但是,同样地,请勿过度担心这一点,依赖于自动装箱是完全可接受的,通常是最佳策略。

record

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

我提到记录仅仅是为了说这个方便的新功能,很可能会变得非常流行,支持对象和原始类型。因此,使用记录不影响原始类型与对象之间的决策。

您的示例类将是:

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团队和相关社区都对模糊原始类型(primitives)和对象之间的差异感兴趣。当然,关键在于在尊重Java平台极高的向后兼容性的同时完成这一点。

其中一些工作正在作为 Valhalla 项目 的一部分进行,包括可能在Java中添加值类型

要了解更多有关此方面的可能未来方向,请查看由Oracle的Brian Goetz等人提供的演讲。使用您喜欢的搜索引擎搜索“Java JEP primitive”,您将找到以下JEP的链接:


非常感谢您提供完美的解释,我投了一票。 - user15807010

9

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作为默认值会令人困惑,因此在这种情况下使用对象是有意义的。如果对象值为“null”,您可以确定该值未初始化。但是,如果原始值为“0”,则不清楚:它是默认未初始化的值,还是该值已用“0”初始化。
通常,我更喜欢使用基本数据类型,除非需要显式空检查。

4
正如其他人所指出的,你可以使用任何一种方法。
正如Basil Bourque所注释的那样,如果你选择使用内存使用量(或其他运行效率度量)作为决策的依据...这很可能是过早优化。很有可能差异太小而无关紧要。

那该怎么决定呢?

这取决于你。但是我的原则是:

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

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

  • 如果没有特定的理由要使用对象类型(例如 Integer),请使用基本类型(例如 int)。换句话说,这是我的默认选择。


为什么我的默认选择是使用原始类型?

因为像这样的原因:

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 - 另一方面,如果你支持“未设置”,那么你的代码会变得更加复杂。当你使用可空类型来实现这一点时,你必须记住在使用(裸露的)字段的任何地方进行null测试并适当处理它。除非有明确的业务需求需要此功能,否则最好设计你的系统,使得“未设置”字段不可能出现。

非常感谢您提供的完美解释,已点赞。 - user15807010

0

在您的情况下,我认为没有使用对象的理由,但也不认为使用它们会对性能产生很大影响。就个人而言,如果我没有特定的原因使用对象,比如需要一个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 }

1
但就我所见,人们似乎更喜欢使用 List<T> 而不是 Array([])。有什么想法吗? - user15807010
1
类似 ArrayList<T> 的集合是可变的,因此它们更易于使用:您可以轻松地添加和删除元素(例如,通过 List<T>.add(T e) 等方法)。数组([])是不可变的,因此每当您需要扩展/收缩集合时,实际上必须声明一个新的数组实例,其大小为新大小,并将元素放置在其中。 - vladek

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