public class Student {
private Long id; // long ?
private String name;
private Boolean isGraduated; // boolean ?
private Integer age; // int ?
}
public class Student {
private Long id; // long ?
private String name;
private Boolean isGraduated; // boolean ?
private Integer age; // int ?
}
正如您所指出的,原始类型需要更少的内存。但是对象在某些情况下具有优势,特别是在期望使用对象而不是原始类型的情况下。在某些情况下,可能会期望使用原始类型,尽管自动装箱通常会处理这种情况。编译器在遇到这些情况时肯定会警告您。
并且Skliar提出了一个合理的观点,即原始类型具有默认值,而对象没有默认值。根据编码情况,这两者都可能是优势。
请记住,在大多数情况下,您不应该担心这个问题。这样的担忧是过早优化的例子。您几乎可以确定有更重要的事情要担心。
对于许多常见的业务应用程序,内存使用量微不足道。我个人的默认设置是使用对象,因为它们方便并具有各种功能。从我多年来注意到的情况来看,使用情况似乎相当分散,有些人倾向于使用原始类型,有些人倾向于使用对象。
唯一需要思考原始类型与对象的情况是处理大量数据时可能会调用自动装箱的情况。在这种情况下,通过避免无意义的装箱,仅声明int
而不是Integer
可能会产生显着影响。但是,同样地,请勿过度担心这一点,依赖于自动装箱是完全可接受的,通常是最佳策略。
record
Java 16+现在提供了记录功能。记录是编写其主要目的是透明和不可变地传达数据的类的简短方式。您只需声明成员字段的类型和名称。编译器会隐式生成构造函数、getter、equals
和hashCode
方法以及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的链接:
Long和Integer变量的默认值是null。
但是,long和int的默认值为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
那该怎么决定呢?
这取决于你。但是我的原则是:
如果建模告诉你需要能表示“此字段未设置”(或类似情况),那么可以使用(比如)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
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 }
List<T>
而不是 Array
([])。有什么想法吗? - user15807010ArrayList<T>
的集合是可变的,因此它们更易于使用:您可以轻松地添加和删除元素(例如,通过 List<T>.add(T e)
等方法)。数组([]
)是不可变的,因此每当您需要扩展/收缩集合时,实际上必须声明一个新的数组实例,其大小为新大小,并将元素放置在其中。 - vladek
OptionalInt
和类似的容器来处理可选值,这些容器具有自我说明性。 - Sokolov