我看到Java具有Boolean(类)vs boolean(原始)。同样,有一个Integer(类)vs int(原始)。何时使用原始版本与类的最佳实践是什么?除非我有特定的理由(性能?),否则我是否应该基本上一直使用类版本?什么是最常见的,公认的使用方式?
我看到Java具有Boolean(类)vs boolean(原始)。同样,有一个Integer(类)vs int(原始)。何时使用原始版本与类的最佳实践是什么?除非我有特定的理由(性能?),否则我是否应该基本上一直使用类版本?什么是最常见的,公认的使用方式?
Answers:
在有效Java的第5项中,Joshua Bloch说
这课很清楚:相对于装箱的图元,更喜欢图元,并提防意外的自动装箱。
类的一个很好的用途是当将它们用作泛型类型(包括Collection类,例如列表和地图)时,或者当您希望将它们转换为其他类型而没有隐式转换时(例如,Integer
类具有方法doubleValue()
或byteValue()
。
编辑:约书亚·布洛赫(Joshua Bloch)的原因是:
// Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); }
该程序得到了正确的答案,但是由于一个字符的印刷错误,它的速度要慢得多。将该变量
sum
声明为a,Long
而不是along
,这意味着程序构造了大约2 ^ 31个不必要的Long
实例(每次将大约long i
添加到时,大约一个Long sum
)。将总和的声明从更改Long
为long
可以将我的机器上的运行时间从43秒减少到6.8秒。
除非您要处理泛型(除非您知道自动装箱和拆箱!),否则标准做法是使用原语。
遵循该约定有很多充分的理由:
1.避免简单的错误:
有一些微妙的,非直觉的案例通常会吸引初学者。即使是经验丰富的编码人员也会滑入并有时犯下这些错误(希望他们在调试代码并找到错误时会发誓!)。
最常见的错误是使用a == b
而不是a.equals(b)
。人们习惯于a == b
使用基元,因此在使用对象包装器时很容易做到。
Integer a = new Integer(2);
Integer b = new Integer(2);
if (a == b) { // Should be a.equals(b)
// This never gets executed.
}
Integer c = Integer.valueOf(2);
Integer d = Integer.valueOf(2);
if (c == d) { // Should be a.equals(b), but happens to work with these particular values!
// This will get executed
}
Integer e = 1000;
Integer f = 1000;
if (e == f) { // Should be a.equals(b)
// Whether this gets executed depends on which compiler you use!
}
2.可读性:
考虑以下两个示例。大多数人会说第二个更具可读性。
Integer a = 2;
Integer b = 2;
if (!a.equals(b)) {
// ...
}
int c = 2;
int d = 2;
if (c != d) {
// ...
}
3.性能:
事实上,它是速度较慢使用对象封装器原语比只使用原语。您要将对象实例化,方法调用等的成本添加到各处使用的事物中。
克努斯(Knuth)的“……大约有97%的时间说:过早的优化是万恶之源”的说法在这里并不适用。他在谈论使代码(或系统)更加复杂的优化-如果您同意第2点,那么这是使代码减少复杂性的优化!
4.约定:
如果您对99%的其他Java程序员做出不同的风格选择,则有两个缺点:
通常,我会列出一些反对意见,但老实说,我不认为有任何好的理由不遵守这里的约定!
==
。对象应与进行比较equals()
。
equals()
... 进行比较...而是给它们一种解决方法,以便将对象与进行比较可以==
产生预期的结果。
equals()
了第二个代码段并更改了我的投票。
通常我会选择原始元素。但是,使用像Integer
和这样的类的一个特殊之处Boolean
是可以分配null
给那些变量。当然,这意味着你必须做的null
检查,所有的时间,但还是更好地得到一个NullPointerException,而不是有因使用一些逻辑错误int
或boolean
尚未正确初始化变量。
当然,自Java 8以来,您可以(而且应该应该)走得更远,而不是例如Integer
可以使用Optional<Integer>
可能具有或没有值的变量。
另外,它引入了用于null
为这些变量分配“ 未知 ”或“ 通配符 ”值的可能性。在某些情况下,例如在三元逻辑中,这可能很方便。或者您可能想检查某个对象是否与某个模板匹配;在这种情况下,您可以使用null
模板中那些在对象中可以具有任何值的变量。
null
默认分配功能中获益匪浅。相反:最好不要完全“初始化”变量。设置任何默认值,甚至null
,都将关闭编译器……但也阻止它检测到所有代码路径中缺少有用的分配。因此,编译器可能会捕获的错误会进入运行时。
0.0
,或-1
,或Integer.MAX_VALUE
或False
,但是最后您不知道这是默认值还是分配给该变量的实际值。在这很重要的情况下,拥有null
价值可能会更清晰。
null
s带来了很多问题,包括无效的妄想症。)在一个函数中,一个可能在使用时实际上未初始化的变量通常表示未发现的情况。(确定分配分析很简单,因此可能会出现误报。但是您通常可以通过简化逻辑来解决它们,因此。)
用外行的话来说:
当您需要向集合中添加内容时,可以使用包装器。
集合不能保存原语。