Java中基本数据类型和包装类

Posted 林加欣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中基本数据类型和包装类相关的知识,希望对你有一定的参考价值。

参考:深入剖析Java中的装箱和拆箱
Java中基本数据类型和包装类互转中 缓冲机制的使用 
java学习笔记:装箱和拆箱,包装器和缓冲池 
Java 各 类型数据在内存中分配情况详解

一 java内存分配

这里只是在网上找的一些资料; 
Java 中的数据类型分为 
1. 基本类型(原始数据类型) byte short int long float double char boolean 
基本类型的变量持有原始值。 
2. 符合数据类型(引用类型),引用类型持有引用值(即对某个对象的引用,而非对象本身)。

一般Java在内存分配时会涉及到以下区域: 
1. 寄存器:我们在程序中无法控制 
2. 栈:存放基本类型的数据和对象的引用但对象本身不存放在栈中,而是存放在堆中 
3. 堆:存放用new产生的数据 
4. 静态域:存放在对象中用static定义的静态成员 
5. 常量池:存放常量 
6. 非RAM存储:硬盘等永久存储空间 
其中主要是堆,栈的存储。

堆,栈

  1. 函数中定义的一些基本类型的数据变量对象的引用变量都在函数的栈内存中分配。 
    栈的优势是存取速度比堆要快,仅次于直接位于CPU 的寄存器,而且数据可以共享。但是存在栈中的数据大小与生存周期必须是确定的。 
    当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

  2. 堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 
    在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 
    引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序 运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

二 基本数据类型

这种类型是通过诸如 int a=7; 的形式来定义的,称为自动变量。这里自动变量是字面值。不是类的实例,即不是类的引用,这里并没有类的存在。a 是指向一个 int 类型的引用,指向 7 这个字面值。由于其大小确定生存期可知(这些定义在某个程序块中,程序块退出后,字段值就消失),因此存在中. 
由于栈的数据可以共享,因此 int a=3; int b=3; 这段代码,编译器首先处理 int a =3; ,先会在栈中创建一个变量为 a 的引用,然后查找有没有字面值为 3的地址,没有找到,就开辟一个存放 3 这个字面值的地址,然后将a 指向 3 的地址。接下来处理int b =3; 在创建完 b 这个引用变量后,由于栈中已经有 3 这个字面值,便将 b 指向 3 的地址。【定义变量,给变量赋值】

三 包装类数据

Java中的基本类型不是面向对象的,它们只是纯粹的数据,除了数值本身的信息之外,基本类型数据不带有其他信息或者可操作方法。这在实际使用中存在很多不足,为了解决这个不足,* 对每个基本类型都对应了一个引用的类型*,称为装箱基本类型。 
这里写图片描述

1,拆箱,装箱

装箱:根据数据创建对应的包装对象。

Integer i = new Integer (3);
Integer j = 4;//jdk1.5 之后可以通过这种方式自动装箱

拆箱:将包装类型转换为基本数据类型。

int  index2 = j.intValue();
int  index1 = i;//自动拆箱

JDK1.5 为Integer 增加了一个全新的方法:public static Integer valueOf(int i) 自动装箱过程时,编译器调用的是static Integer valueOf(int i)这个方法 于是Integer a=3; ==> Integer a=Integer.valueOf(3);

此方法与new Integer(i)的不同处在于: 
方法一调用类方法返回一个表示 指定的 int 值的 Integer 实例。方法二产生一个新的Integer 对象。

 

2,缓冲机制

 

JDK API文档中对这个新的valueOf方法有明确的解释: 
如果不需要新的 Integer 实例,则通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过缓存经常请求的值而显著提高空间和时间性能 .

查看Integer的valueOf方法的:

    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        //static final int low = -128;
        //当-128=<i<=127的时候,就直接在缓存中取出 i de  Integer 类型对象
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        //否则就在堆内存中创建
        return new Integer(i);
    }

 

看出对于范围 [-128,127] 的整数,valueOf 方法做了特殊处理。采用IntegerCache.cache[i + (-IntegerCache.low)]; 这个方法。

查看 IntegerCache 类的实现为:

    private static class IntegerCache {
        static final int low = -128; //最小值是固定的
        static final int high;
        static final Integer cache[];//cache 缓存是一个存放Integer类型的数组

        static { //初始化,最大值可以配置
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];  //初始化数组
            int j = low;
            //缓存区间数据
            for(int k = 0; k < cache.length; k++)
            //将-128~127包装成256个对象存入缓存
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

 

IntegerCache初始化后内存中就有Integer缓冲区cache[]了,-128~127区间的int值有其对应的的包装对象。这就是 valueOf 方法真正的优化方法,当-128=

public class ZhuangXaing {

    public static void main(String[] args) {
        Integer i= new Integer(12);
        Integer j=12;
        Integer k=Integer.valueOf(12);


        Integer l= new Integer(232);
        Integer m=232;
        Integer n=232;

        Double  q = 232.0;

        System.out.println("use ==.......");    
        System.out.println(i==12);
        System.out.println(i==j);
        System.out.println(j==k);

        System.out.println(l==232);
        System.out.println(l==m);
        System.out.println(m==n);

        System.out.println("use equals.....");
        System.out.println(m.equals(n));
        System.out.println(m.equals(q));

    }

}

输出结果:

use ==.......
true
false
true
true
false
false
use equals.....
true
false

Integer i= new Integer(12); 是指明了在堆内存中创建对象; 
Integer j=12; 是自动装箱,调用valueOf 方法,返回return IntegerCache.cache[12 + 128], 得到的是Integer 缓冲池中的对象。Integer k=Integer.valueOf(12); 与Integer j=12; 本质上相同,指向缓冲池中同一对象。包装对象与数值比较,自动拆箱。 
而对于大于127 的数值,执行的都是return new Integer(i) 都在堆内存中,但是地址不同。

对于equals 方法比较的是数值大小:

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

 

可以看出比较的 obj 如果是 Integer 的实例,则比较拆箱后数值的是否相等。否则返回false。

2,下面这段代码输出结果是什么:

public class Main {
    public static void main(String[] args) {

        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
//false
//false

 

因为Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

其他的包装器: 
Boolean: (全部缓存) 
Byte: (全部缓存)

Character ( <=127 缓存) 
Short (-128~127 缓存) 
Long (-128~127 缓存)

Float (没有缓存) 
Doulbe (没有缓存)

3,下面这段代码输出结果是什么:

public class Main {
    public static void main(String[] args) {

        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;

        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

 

先看Boolean 类的源码 ,valueOf 方法的实现:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

 

而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

public static final Boolean TRUE = new Boolean(true);

    /** 
     * The <code>Boolean</code> object corresponding to the primitive 
     * value <code>false</code>. 
     */
    public static final Boolean FALSE = new Boolean(false);

 

由此可知上面代码输出都为true .

以上是关于Java中基本数据类型和包装类的主要内容,如果未能解决你的问题,请参考以下文章

java 基本数据类型对象包装类

Java var定义基本数据类型变量实际类型问题(原始类型或包装类)

Java基础(34):Java中基本数据类型的包装类(主要为了不同数据类型之间更方便的进行转换)

Java包装类之实体类不要使用基本类型

Java中的基本类型和包装类

java基本数据类型和包装类之间的区别