这是Thinging in Java学习笔记的第二篇,主要是关于基本类型的。
不足之处,欢迎斧正。

        不少人(包括我自己)学到了变量之后会很奇怪Java不是面向对象语言吗,为什么有int这种极具C语言(面向过程语言)气息的变量?

        既然有int那就直接用int就好了嘛,为什么还要装箱(Boxing)和拆箱(Unboxing)?

int -> 面对对象?

        前一篇博客中提到了面向对象语言的一个特性 “Everything is an Object.”

        从前文也可以推断出这个特性并不是绝对的,在everything之外也是有特例的,这个特例就是基本类型(Primitive Types)。

        在程序设计中经常会使用到一系列类型,它们需要被特殊对待,我们可以把它们想象成“基本”(primitive)类型。之所以特殊对待,是因为new将对象存储在“堆”里,故用new创建一个对象——特别是小的、简单的变量,往往不是很有效。

        这么高深的话像我等俗人是理解不来的,所以我把它翻译了一下就是我如果用个简简单单的int还要去创造一个类,去new出来,然后再去赋值太麻烦了,人累、编译器也累。

        当然这个原因并不能支持一门纯粹的面向对象语言去保留面向过程语言的特性,我觉得它还有其他的原因。在解释这些原因之前我们需要先了解一下这些类型所占用的内存空间大小。

        Reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes。

        因为Java的Object将基本的数据都包装了一遍,导致我们在使用这些包装类的时候占用了更多的内存以及使用其中的数据的时候占用了更多的时间。

        下面是关于时间的测试代码:

public class Main {
    public static void main(String[] args) {
        a();
        b();
    }
    public static void a(){
        long startTime=System.nanoTime();
        for(int i=1;i<10000;i++){
            int a = 1,b = 2;
            int c = a + b;
        }
        long endTime=System.nanoTime();
        System.out.println("程序运行1时间: "+(endTime-startTime)+"ns");
    }
    public static void b(){
        long startTime=System.nanoTime();
        for(int i=1;i<10000;i++){
            Integer a = 1,b = 2;
            Integer c = a + b;
        }
        long endTime=System.nanoTime();
        System.out.println("程序运行2时间: "+(endTime-startTime)+"ns");
    }
}
/*
 * 程序1运行时间: 105223ns
 * 程序2运行时间: 1464137ns
 */

        可以看出,在相同次数的操作下Integer的所用的时间是int的14倍以上。

        当然,会造成的问题不仅仅如此,比如下面这个例子。

Integer i1 = 500;
Integer i2 = 500;
System.out.println( i1==i2 );  //false

        emmm,500不是500。当然这个并不是一个大问题,换成equal就行了,至于原因和JVM虚拟机有关。

总结一下为什么存在这些基本类型的原因:

  • 内存占用问题
  • 速度问题
  • 变量比较问题
  • 开发效率问题

抛弃基本类型

        从上面的原因看来好像如果为了面向对象的纯粹也是可以抛弃基本类型也是可以接受的,但事实上,并没有这么简单。

        首先,我们如果全部淘汰了基本类型,基本上所有的旧程序都不能允许了,这个代价并不是哪一方可以承受的。

        其次,为了支持这个新特性,需要把重写整个JVM虚拟机。

        最后,这个特性用了这么久了,即没出现什么大的问题,还可以减轻这么多开发的代价,为什么要吃力不讨好去抛弃它呢。

装箱(Boxing)和拆箱(Unboxing)

        既然选择了保留基本类型这一特性,那么避免不了的这些基本类型得去向其他的对象(Object)发信息,可是两个世界的东西要怎么发送信息呢?

        这时,装箱和拆箱的作用就体现出来了。装箱可以将int等基本类型包装成对象,使其和其他对象一样也有状态和行为;类似的,拆箱就是将被包装好的Integer等对象拆成基本的int等类型。

        得益于Java的自动装箱和自动拆箱,我们可以很轻松等进行转换。

int i1 = 500;

Integer i2 = i1;
执行上面那句代码的时候,系统为我们执行了:
Integer i2 = Integer.valueOf(i1);

int i3 = i2;
执行上面那句代码的时候,系统为我们执行了:
Integer i2 = i2.intValue();;

这里需要注意的是:

Integer i4 = new Integer(10);

已经是过时的了,Integer.java 里面的说明是:

参考资料

《Thinking in Java》 Fourth Edition ——Bruce Eckel

10-07 12:30