数组和内存控制

数组与内存控制

使用Java数据之前必须先对数组对象进行初始化。当数组的所有元素都被分配了合适的内存空间,并指定了初始值时,数组初始化完成。程序以后将不能重新改变数组对象在内存中的位置和大小。从用法角度来看,数组元素相当于普通变量,程序既可以把数组元素的值赋给普通变量,也可把普通变量的值赋给数组元素。(注意数组变量数组对象数组元素的区别)

1.1 数组初始化

如果程序需要多个类型相同的变量时,就可以考虑定义一个数组。Java语言的数组变量是引用类型的变量,因此具有Java独有的特性。

1.1.1 Java数组是静态的

Java语言是典型的静态语言,因此Java的数组是静态的,即当数组被初始化之后,该数组的长度是不可变的。Java程序中的数组必须经过初始化才可使用。所谓初始化,就是为数组对象的元素分配内存空间,并为每个数组元素指定初始值。

数组的初始化有以下两种方式。

n  静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。

n  动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。

不管采用哪种方式初始化Java数组,一旦初始化完成,该数组的长度就不可改变,Java语言允许通过数组的length属性来访问数组的长度。

public classArrayTester {

 

  public static void main(String[] args) {

 

      // 采用静态初始化方式初始化数组

      int[] array1 = new int[] { 1, 2, 3 };

 

      // 采用静态初始化简写方式初始化数组

      int[] array2 = { 1, 2, 3, 4 };

 

      // 采用动态初始化方式初始化数组

      int[] array3 = new int[5];

      array3[0] = 1;

      array3[1] = 2;

      array3[2] = 3;

      array3[3] = 4;

      array3[4] = 5;

 

      System.out.println("array1.length:" + array1.length);

      System.out.println("array2.length:" + array2.length);

      System.out.println("array3.length:" + array3.length);

 

  }

 

}

运行结果:

array1.length:3

array2.length:4

array3.length:5

对于静态初始化方式而言,程序员无需指定数组长度,指定该数组的数组元素,由系统来决定该数组的长度即可。

执行动态初始化时,程序员只需指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组元素分配初始值。

指定初始值时,系统将按如下规则分配初始值。

n  基本类型中的整数类型(byte、short、int、long),则数组元素的值是0。

n  基本类型中的浮点类型(float、double),则数组元素的值是0.0。

n  基本类型中的字符类型(char),则数组元素的值是'\u0000'。

n  基本类型中的布尔类型(boolean),则数组元素的值是false。

n  引用类型(类、接口和数组),则数组元素的值是null。

注意:不要同时使用静态初始化和动态初始化,也就是说,不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值。

Java的数组是静态的,一旦数组初始化完成,数组元素的内存空间分配即结束,程序只能改变数组元素的值,而无法改变数组的长度。

Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象。因此,可以改变一个数组变量所引用的数组,这样可以造成数组长度可变的假象。

public classArrayTester {

 

  public static void main(String[] args) {

 

      //采用静态初始化方式初始化数组

      int[] array1 = new int[] { 1, 2, 3 };

 

      //采用静态初始化简写方式初始化数组

      int[] array2 = { 1, 2, 3, 4 };

 

      //采用动态初始化方式初始化数组

      int[] array3 = new int[5];

      array3[0] = 1;

      array3[1] = 2;

      array3[2] = 3;

      array3[3] = 4;

      array3[4] = 5;

 

      System.out.println("array1.length:" + array1.length);

      System.out.println("array2.length:" + array2.length);

      System.out.println("array3.length:" + array3.length);

     

      array2= array1;

      array3 = array1;

     

      System.out.println("array1.length:" + array1.length);

      System.out.println("array2.length:" + array2.length);

      System.out.println("array3.length:" + array3.length);

     

     

 

  }

 

}

运行结果:

array1.length:3

array2.length:4

array3.length:5

array1.length:3

array2.length:3

array3.length:3

1.1.2 数组一定要初始化吗

使用Java数组之前必须先初始化数组,也就是为数组元素分配内存空间,并指定初始值。

实际上,如果真正掌握了Java数组在内存中分配机制,那么完全可以换一个方式来初始化数组,或者说,数组无需经过初始化。

始终记住Java的数组变量是引用类型的变量,它并不是数组对象本身,只要让数组变量指向有效的数组对象,程序中即可使用该数组变量。

public classArrayTester {

 

  public static void main(String[] args) {

 

      int[] array1 = new int[] { 1, 2, 3 };

 

      int[] array2 = null;

 

      //指向有效的数组对象

      array2 = array1;

      for (int i = 0; i < array2.length; i++) {

         System.out.print(array2[i] + "\t");

      }

      System.out.println();

 

  }

 

}

运行结果:

1  2   3

注意:数组变量只是一个引用变量,通常存放在栈内存中(也可以被存放入堆内存中),而数组对象是保存在堆内存中的连续内存空间。对数组执行初始化,其实并不是对数组变量执行初始化,而是对数组对象执行初始化——也就是为该对象分配一块连接的内存空间,这块连接内存空间的长度就是数组的长度。

1.1.3 基本类型数组的初始化

对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此基本类型数组的初始化比较简单:程序直接先为数组分配内存空间,再将数组元素的值存入对应内存里。

public classArrayTester {

 

  public static void main(String[] args) {

 

      int[] array = new int[3];

 

      array[0] = 1;

      array[1] = 2;

      array[2] = 3;

 

  }

 

}

注意:所有局部变量都是存放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中;但引用类型变量所引用的对象(数组、普通Java对象)则总是存储在堆内存中。

对于很多Java程序员而言,他们最容易混淆的是:引用类型变量何时只是栈内存中的变量本身,何时又变为引用实际的Java对象。其实规则很简单:引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或者通过调用引用变量来调用方法,该引用变量将会由它所引用的对象代替。

1.1.4 引用类型数组的初始化

引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象(包括数组和Java数组)。

对于引用类型的数组而言,它的数组元素其实就是一个引用类型的变量,因此可以指向任何有效的内存——此处“有效”的意思指强类型的约束。比如对String[]类型的数组而言,它的每个数组元素都相当于String类型的变量,因此它的数组元素只能指向String对象。

1.2 使用数组

当数组引用变量指向一个有效的数组对象之后,程序就可以通过该数组引用变量来访问数组对象。Java语言不允许直接访问堆内存中的数据,因此无法直接访问堆内存中的数组对象,程序将通过数组引用变量来访问数组。

1.2.1 数组元素就是变量

只要在已有的数据类型之后增加方括号,就会产生一个新的数组类型,示例如下。

n  int->int[]:在int类型后增加[]即变为int[]数组类型。

n  String->String[]:在String类型后增加[]即变为String[]数组类型。

n  Person->Person[]:在Person类型后增加[]即变为Person[]数组类型。

n  int[]->int[][]:在int[]类型后增加[]即变为int[][]数组类型。

当程序需要多个类型相同的变量来保存程序的状态时,可以考虑使用数组来保存这些变量。当一个数组初始化完成,就相当于定义了多个类型相同的变量。

无论哪种类型的数组,其数组元素其实相当于一个普通变量,把数组类型之后的方括号去掉后得到的类型就是该数组元素的类型。示例如下。

n  int[]->int:int[]数组的元素相当于int类型的变量。

n  String[]->String:String[]数组的元素相当于String类型的变量。

n  Person[]->Person:Person[]数组的元素相当于Person类型的变量。

n  int[][]->int[]:int[][]数组的元素相当于int[]类型的变量。

当通过索引来使用数组元素时,将该数组元素当成普通变量使用即可,包括访问该数组元素的值,为数组元素赋值,等等。

需要指出的是,main方法声明的变量都属于局部变量,因此它们都被保存在main方法栈中,但数组元素则作为数组对象的一部分,总是保存在堆内存中,不管它们是基本类型的数组元素,还是引用类型的数组元素。

1.2.2 没有多维数组

只要在已有数据类型之后增加方括号,就会产生一个新的数组类型。如果已有的类型是int,增加方括号是int[]类型,这是一个数组类型;如果再以int[]类型为已有类型,增加方括号就得到int[][]类型,这依然是数组类型;如果再以int[][]类型为已有类型,增加方括号就得到int[][][]类型,这依然是数组类型。

反过来,将数组类型最后的方括号去掉就得到了数组元素的类型。对于int[][][]类型的数组,其数组元素就相当于int[][]类型的变量;对于int[][]类型的数组,其数组元素就相当于int[]类型的变量;对于int[]类型的数组,其数组元素就相当于int[]类型的变量。

从上面分析可以看出,所谓多维数组,其实只是数组元素依然是数组的1维数组:2维数组是数组元素是1维数组的数组,3维数组是数组元素是2维数组的数组,4维数组是数组元素是3维数组的数组......N维数组是数组元素是N-1维数组的数组

Java允许将多维数组当成1维数组处理。初始化多维数组时可以只初始化最左边的维数,此时该数组的每个元素都相当于一个数组引用变量,这些数组元素还需要进一步初始化。

public classArrayTester {

 

  public static void main(String[] args) {

 

      int[][] array = new int[3][];

 

      for (int i = 0; i < array.length; i++) {

         System.out.print(array[i] + "\t");

      }

      System.out.println();

     

      for(int i=0;i<array.length;i++){

         array[i] = new int[i+1];

      }

     

      for(int i=0;i<array.length;i++){

         for(int j=0;j<array[i].length;j++){

             array[i][j] = i*j + 1;

         }

      }

     

      for(int i=0;i<array.length;i++){

         for(int j=0;j<array[i].length;j++){

             System.out.print(array[i][j] + ");

         }

         System.out.println();

      }

 

  }

 

}

运行结果:

null  null   null  

1  2 

1  3 5 

如果定义一个Object[]类型的数组,此时,每个数组元素都相当于一个Object类型的引用变量,因此可以指向任何对象(包括数组对象和普通Java对象)。

public classObjectArrayTest

{

  public static void main(String[] args)

  {

      //定义、并初始化一个Object数组

      Object[] objArr = new Object[3];

      //让objArr所引用数组的第二个元素再次指向一个长度为2的Object[]数组

      objArr[1] = new Object[2];

      //将objArr[1]的值赋给objArr2,即让objArr2和objArr[1]指向同一个数组对象

      Object[] objArr2 = (Object[])objArr[1];

      //让objArr2所引用数组的第二个元素再次指向一个长度为3的Object[]数组

      objArr2[1] = new Object[3];

      //将objArr2[1]的值赋给objArr3,即让objArr3和objArr2[1]指向同一个数组对象

      Object[] objArr3 = (Object[])objArr2[1];

      //让objArr2所引用数组的第二个元素再次指向一个长度为3的int[]数组

      objArr3[1] = new int[5];

      将objArr3[1]的值赋给iArr,即让iArr和objArr3[1]指向同一个数组对象

      int[] iArr = (int[])objArr3[1];

      //依次为iArr数组的每个元素赋值

      for (int i = 0 ; i < iArr.length ; i++)

      {

         iArr[i] = i * 3 + 1;

      }

      //直接通过objArr访问iArr数组的第3个元素

      System.out.println(((int[])((Object[])((Object[])objArr[1])[1])[1])[2] );

  }

}

之所以需要经过多次强制类型转化,是因为Java是强类型语言。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值