mc为什么要用java_Java为什么需要保留基本数据类型(一)

基本数据类型对以数值计算为主的应用程序来说是必不可少的。

自从1996年Java发布以来,基本数据类型就是Java语言的一部分。John

Moore通过对使用基本类型和不使用基本类型做java基准测试给Java中为什么要保留基本数据类型做了一个很有力的说明。然后,他还在特定类型的应用中把Java和Scala、C++和JavaScript的性能做了对比。在这些应用中,使用基本数据类型应用性能会有很显著的不同。

问:影响买房最重要的三个因素是什么?

答:位置!位置!还是位置!!

这是个很古老但却经常被提及的谚语。意思是,当购买房产的时候,位置因素是绝对的主导因素。与此类似,考虑在Java中使用基本数据类型的3个最重要的因素,那就是性能!性能!还是性能!!房产与基本数据类型有两个不同之处。首先,位置主导几乎适用于所有买房的情况。但是使用基本类型带来的性能提升,对不同类型的应用程序来大不相同。其次,尽管其他的因素与位置相比在买房时都显得不太重要,但是也是应该被考虑的因素。而使用基本数据类型的原因却只有一个——那就是性能,并且只适用于那些使用后能提升性能的应用。

基本数据类型对大多数业务相关或网络应用程序没有太大的用处,这些应用一般是采用客户端/服务器模式,后端有数据库。然而,使用基本数据类型对那种以数值计算为主的应用提升性能有很大好处。

在Java语言中包含基本数据类型是最有争议的设计决定之一,关于这个决定讨论的文章和帖子比比皆是。2011年9月,Simon Ritter在JAX London上的演讲说,要认真考虑在Java未来的某个版本中删除基本数据类型。接下来,我会简要介绍基本数据类型和Java的双类型系统。通过示例代码和简单的基准测试说明为什么基本数据类型对于某些类型的应用程序是必须的。我也会对Java与Scala、C++和JavaScript性能做一些比较。

测量软件性能

软件性能经常用时间和空间来衡量。时间可以是实际的运行时间,比如3.7分钟,或者是基于输入规模的时间复杂度,比如O(n2)。对于空间的衡量也是如此。经常用内存消耗来衡量,有时候也会扩大到磁盘的使用。改善性能经常要做时间和空间的折衷,缩短时间经常会对空间造成损害,反之亦然。时间复杂度取决于算法,把包装类型切换成基本数据类型对结果不会有任何改变,但是使用基本数据类型取代对象类型能改善时间和空间的性能。

基本数据类型vs对象类型

当你阅读这篇文章的时候,可能已经知道了Java是双类型的系统,也就是基本数据类型和对象类型,简称基本类型和对象。Java中有8个预定义的基本类型,它们的名字都是保留的关键字。常见的基本类型有int、double和boolean。Java中所有其他的类型包括用户自定义的类型,它们必然也是对象类型(我说”必然”是因为数组类型有点例外,与基本类型比数组更像是对象类型)。每一个基本类型都有一个对应的对象包装类,比如int的包装类是Integer,double的包装类是Double,boolean的包装类是Boolean。

基本类型基于值,而对象类型则基于引用。与基本类型相关的争议都源于此。为了说明它们的不同,先来看一下两个声明语句。第一个语句使用的是基本类型,第二个使用的是包装类。

1

2

int n1 = 100;

Integer n2 = new Integer(100);

使用新添加到JDK5的特性自动装箱以后,第二个声明可以简化成:

1

Integer n2 = 100;

但是,底层的语义并没有发生改变。自动装箱简化了包装类的使用,减少了程序员的编码量,但是对运行时并没有任何的改变。

图1展示了基本类型n1和包装对象类型n2的区别。

a4c26d1e5885305701be709a3d33442f.png图1.

基本类型vs对象类型的内存结构

n1持有一个整数的值,但是n2持有的是对一个对象的引用,即那个对象持有整数的值。除此之外,n2引用的对象也包含了一个对Double对象的引用。

基本数据类型存在的问题

在我试图说服你需要基本类型之前,首先我应该感谢不同意我观点的那些人。Sherman Alpert在”基本类型是有害的(Primitive types considered

harmful)”这篇文章中说基本类型是有害的,因为“它们把函数式的语义混进了面向对象模型里面,让面向对象变得不纯。基本类型不是对象,但是它们却存在于以一流对象为根本的语言中”。基本类型和(包装类形式的)对象类型提供了两种处理逻辑上相似的类型的方式,但是在底层的语义上却有着非常大的不同。比如,两个实例如何来比较相等性?对于基本类型,使用==操作符,但是对于对象类型,更好的方式是调用equals()方法,而基本类型是没有这个操作的。相似的,在赋值和传参的语义上也是不同的。就连默认值也是不一样的,比如int的默认是值0,但是Integer的默认值是null。

关于这个话题的更多的背景可以参考Eric Bruno的博客”关于基本类型的讨论(A modern primitive

discussion)”,里面总结了关于基本类型的正反两方面的意见。Stack

Overflow上也有很多关于基本类型的讨论,包括”为什么人们仍然在Java中使用基本类型?(Why do people still use

primitive types in Java?)”,”有没有只使用对象不使用基本类型的理由?(Is there a reason to always

use Objects instead of primitives?)”。Programmers Stack

Exchange上也有一个类似的叫做”Java中什么时候应该使用基本类型和对象类型?(When to use primitive

vs class in Java?)”的讨论。

内存的使用

Java中的double总是占据内存的64个比特,但是引用类型的字节数取决于JVM。我的电脑运行64位Win7和64位JVM,因此在我的电脑上一个引用占用64个比特。根据图1,一个double比如n1要占用8个字节(64比特),一个Double比如n2要占用24个字节——对象的引用占8个字节,对象中的double的值占8个字节,对象中对Double对象的引用占8个字节。此外,Java需要使用额外的内存来支持对象的垃圾回收,但是基本类型不需要。下面让我们来验证下。

跟Glen McCluskey在”Java基本类型 VS 包装类型(Java primitive types vs.

wrappers)”中使用的方式类似,列表1中的方法会测量一个n*n的double类型的矩阵(二维数组)所占的字节数。

列表1. 计算double类型的内存使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public static long getBytesUsingPrimitives(int

n)

{

System.gc();

// force garbage collection

long

memStart = Runtime.getRuntime().freeMemory();

double[][]

a = new double[n][n];

//

put some random values in the matrix

for

(int

i = 0; i < n; ++i)

{

for

(int

j = 0;

j < n; ++j)

a[i][j]

= Math.random();

}

long

memEnd = Runtime.getRuntime().freeMemory();

return

memStart - memEnd;

}

修改列表1中的代码(没有列出来),改变矩阵的元素类型,我们也可以测量一个nn的Double类型的矩阵所占的字节数。在我的电脑上用10001000的矩阵来测试这两个方法,我得到了表1的结果。就像之前说的那样,基本类型版本的double矩阵中每一个元素占8个多字节,跟我预期的差不多,但是,对象类型版本的Double矩阵中每一个元素占28个字节还要多一点。因此,在这个例子中,Double的内存使用是double的3倍还要多,这对那些明白上面图1说的内存布局的人来说,并不是一件让人很吃惊的事情。

表1. double和Double的内存使用情况对比

版本

总字节数

平均字节数

使用double

8,380,768

8.381

使用Double

28,166,072

28.166

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值