深入数组(引用类型)

深入数组(引用类型)

数组是一种引用数据类型,数据引用变量只是一个引用,数组元素和数组变量在内存里是分开存放的。在我们看待一个数组时,一定要把他看待成两部分:一部分是数组引用,也就是在代码中定义的数组引用变量;还有一部分是实际的数组对象,这部分是在堆内存中运行的,通常无法直接访问它,只能通过数组引用变量来访问。

  • 内存中的数组

    数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可以通过该数组变量来访问数组元素。

    与所有引用变量相同的是,引用变量是访问真实对象的根本方式。也就是说,如果希望在程序中访问数组对象本身,则只能通过这个数组的引用变量来访问它。

    实际的数组对象被存储在堆内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它被存储在栈内存中。

    也就是说,数组引用变量是访问堆内存的根本方式。

    如果堆内存中数组不再有引用变量指向自己,则这个数组将变为垃圾。该数组所占的内存将会被系统的垃圾回收器回收。因此为了让垃圾回收器回收一个数组所占的内存空间,可以将该数组变量赋为null,也就切断了数组引用变量和实际变量之间的引用关系,实际的数组也就变成了垃圾。

  • 只要类型互相兼容,就可以让一个数组变量指向另一个实际的数组,这样的操作会让人产生数组的长度可变的错觉。

    public class ArrayInRam
    {
        public static void main(String[] args)
        {
            int[] a={5,7,20};
            int [] b=new int[4];
            System.out.println("b数组的长度为:" + b.length);//输出b数组的长度
            System.out.println("a数组的长度为:" + a.length);
            for(int i=0;i<a.length;i++)
            {
                System.out.println(a[i]);
            }
            for(int i=0,len=b.length;i<len;i++)·
            {
                System.out.println(b[i]);
            }
            //a数组是int类型,b数组也是int类型,可以将a的值赋给b
            //也就是让b指向a引用指向的数组
            b=a;
            System.out.println("b数组的长度为:"+b.length);
            System.out.println("a数组的长度为:" + a.length);//这里变化的是引用变量
        }
    }
    

    可见两个同类型的数组可直接进行赋值,看起数组的长度是可变的,但是不要忘记一个数组有两个内存,一个是在栈内存中的引用变量,一个是对内存引用变量所指向的数组本身。实际上在堆内存的数组长度仍然没有发生改变。

    我们在程序区域可操作的是栈内存,底层的堆内存我们无法直接对他们进行操作,但是可通过栈内存的引用变量来实现间接操作。

    如上程序可见。引用变量a赋给了引用变量b,也就是说在之前引用变量a指向堆内存的a数组有两个栈内存的引用变量,即:a和b。

    此时堆内存的b数组没有指向他的引用变量,变成了垃圾,等待被回收,但它的长度仍然没有发生改变,直到他彻底消失。

  • 基本类型数组的初始化

    对于基本类型数组而言,数组元素的值直接储存在对应的数组中,因此,初始化数组时,先为该数组分配空间,然后直接将数组元素的值存入对于的数组元素中。

    public class PremitiveArrayTest
    {
        public static void main(String[] args)
        {
            int [] iArr;
            iArr=new int [5];
            for(var i=0;i<iArr.length;i++)
            {
                iArr[i]=i+10;
            }
        }
    }
    

    执行第一行int声明iArr数组后,仅在栈内存中定义了一个空引用(就是iArr数组变量),这个引用并未指向任何有效的内存,当然无法指向数组的长度。

    当执行iArr=new int [5];动态初始化后,系统将负责为该数组的每个数组元素依次赋值为0;此时堆内存中的数组是有值的,每个数组元素的值都是0,

    当循环为每个数组元素赋值后,此时每个数组元素的值都变成程序显式指定的值。显示指定每个数组元素值后方的存储。

    因此可以看出每个数组元素的值直接存储在对应的内存中。操作基本类型数组的数组元素时,实际上相当于操作基本类型的变量。

  • 引用类型数组的初始化

    引用类型数组的数组元素是引用,因此情况变得更加复杂。每个数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了有效数据。

    为了更好低说明引用类型数组的运行过程,下面先定义一个Person类(所有的类都是引用类型)。

    class Person
    {
        public int age;
        public double height;
        public void info()
        {
            System.out.println("我的年龄是:" + age +"我的身高是:" +height);
    }
    }
    

    下面程序将定义一个Person[]数组,接着动态舒适化这个Person[]数组,并为这个数组的每个数组元素指定值。程序代码如下

    public class ReferenceArrayTest
    {
        public static void main(String[] args)
        {
            Person[] students;//定义一个students数组,其类型为Person
            students = new Person[2];//执行动态初始化
            var zhang= new Person();//创建一个Person实例,并将这个实例赋给zhang变量
            zhang.age=15;
            zhang.height=158;
            var lee =new Person();//创建一个Person实例,并将这个实例赋给lee变量
            lee.age=16;//为lee所引用的Person对象的age、height赋值
            lee.height=161;
            students[0]=zhang;//将zhang、lee等变量的值赋给数组元素
            students[1]=lee;
            lee.info();//这两行代码的值完全一样,因为lee和students[1]指向的是用一个Person实例
            students[1].info();
        } 
    }
    

    上述代码的执行过程代表了引用类型数组舒适化的典型过程。

    执行Person[]students;代码时,这行代码仅仅在栈内存中定义了一个引用变量,这就是一个指针,这个指针并为指向任何有效的内存区。

    栈内存中定义了一个students变量。它仅仅是一个引用,并未指向任何有效的内存,直到执行初始化,本程序对students数组执行动态初始化,动态初始化由系统为数组元素分配默认的初始值,:null。即每个数组元素的值都是null。

    students数组的两个数组元素都是引用,而且这个引用并未指向任何有效的内存,因此每个数组元素的值都是null。这相当于定义了两个连续的Person变量,但这个变量还未指向任何有效的内存区,所以这两个连续的Person变量(students数组的数组元素)还不能使用。

    接着代码定义了lee和zhang两个Person的实例,定义在这两个实例实际上分配了4块内存,在栈内存中存储了zhang和lee两个引用变量,还在堆内存中存储了两个Person实例。

    此时students数组的两个元素依然是null,直到程序以此将zhang和lee赋给数组的第一、二个元素,students数组的两个数组元素将会指向有效的内存区。

  • 数组的名称

    直接打印数组名称:得到的是数组对应的内存地址哈希值。

    格式[数据类型首字母+@+16进制地址值。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值