【Java】内存中的数组

在讲内存中的数组之前,我们了解一下内存,Java是怎么使用内存的呢?简单的介绍一下java的内存机制。在Java里面把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量...

在讲内存中的数组之前,我们了解一下内存,Java是怎么使用内存的呢?简单的介绍一下java的内存机制。在Java里面把内存划分成两种:一种是栈内存,另一种是堆内存。

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被其他变量使用。

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

这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针,只不过被隐藏了,我们看不到。

1、基本数据类型数组

再来讲一下数组,数组是一种引用类型,既然是引用类型,那就存在两部分,引用变量和变量的值。引用变量为数组的名称,存放在栈内存中,变量的值也就是数组中存放的数据,存放在堆内存中。当一个堆内存中的对象没有任何引用变量引用它时,垃圾回收器就会在合适的时候回收这个对象占用的内存。

int[]  a;
a = new int[5];

第一行代码int[] a只是声明定义了一个数组,并没有为这个数组赋值,内存中的变化如下图所示;

执行代码int[] a后,只是在栈内存中定义了一个空引用,这个引用并没有只想任何有效的内存,这时候如果使用这个指针的话,就会抛出空指针异常(NullPointerException)。

当执行a = new int[5]这段代码后,数组就会被动态初始化,系统会给数组分配内存空间,并且会有默认值。这时候内存中存储示意图如下所示:

这就是基本类型数组的初始化,看起来比较简单,接着看一下引用类型的数组。

2、引用类型数组

当引用类型的数组初始化时,情况会复杂一些,再举一个例子,首先定义一个类Num,所有的类都是引用类型。

public class Num {
   public int i;
   public double d;
}

 接着定义一个对象数组,并对这个数组进行初始化操作,接着给每个元素指定一个值。代码如下所示:

public static void main(String[] args) {
        //定义一个对象数组nums
        Num[] nums;
        //动态初始化操作
        nums = new Num[2];
        //创建两个Num实例,并给相应属性赋值
        Num big = new Num();
        big.i = 8;
        big.d = 8.8;
        Num sma = new Num();
        sma.i = 1;
        sma.d = 1.1;

        //将big赋值给第一个数组元素,将sma赋值给第二个数组元素
        nums[0] = big;
        nums[1] = sma;
        //比较结果为true
        System.out.println(nums[0] == big);
    }

结合数组在内存中的变化情况,分析一下代码的执行过程。

首先当我们定义数组时,执行Num[] nums;代码,这时候在栈内存中定义了一个引用变量,就像C语言中的指针,而且还是一个空指针。

动态初始化以后,系统会分配地址,指针就有地址了,因为数组里面放的引用变量,所以数组元素默认值为null,这时候还不能使用数组里面的元素。

然后定义了两个实例变量big和sma,根据上面的解释,我们会发现这时候使用了四块内存。堆内存里面放了两个Num的实例。

接着执行下面的代码,把big赋给属猪中的第一个元素,把sma赋给数组中的第二个元素。这时候数组中的元素就不再是空了。

 如上图所示,这时候big和nums[0]指向同一块内存区,所以这两个变量代表同一个对象,所以对比的结果为true;这时候如果修改big变量所指向的实例或者nums[0]指向的实例,都是修改同一块区域,再调用另一个引用变量时,已经是修改以后的实例了,实际使用过程中需要多加注意。

根据上面的结果,我们可以想到,在内存中,多维数组的存储也是同样的道理。

更多知识可以关注一下我的公众号【三更编程菌】。

 

本文标题为:【Java】内存中的数组