疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java数组与内存控制


 

  java语言是典型的静态语言,因此java的数组是静态的。即当数组被初始化后,改数组的长度是不可变的。

  java程序的数组必须经过初始化后才能使用。初始化是指:为数组对象的元素分配内存空间,并为每个元素指定初始值。

  数组的初始化有两种:静态初始化、动态初始化

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

  例子:String[] books = new String[]{"疯狂java","hello kity"};

  String[] name = {"river","sea"};

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

  例子:String[] strArr = new String[5];

  系统分配初始值规定:

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

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

  数组元素类型是基本类型中的字符类型(char)则数组元素的值是‘u0000’

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

  java数组是静态的,一旦为数组初始化完成,就无法改变数组的长度。

  但是,需要指出的是,java的数组变量是一种引用型的变量,数组变量并不是数组本身。他只是指向堆内存中的数组对象。

  因此,可以改变一个数组变量所引用的数组,这样可以造成数组长度可变的假象

  对数组变量来说,他们并不需要进行所谓的初始化,只要让数组变量指向一个有效的数组对象,程序即可正常使用该数组变量。

  需要初始化的永远只是该引用变量所引用的对象,而不是引用变量本身。

  来看一个动态语言数组的例子,

  javascript是一种动态语言,他的数组长度是可以动态改变的,程序如下:

  var arr=[];

  document.writeln("arr的长度是:"+arr.length+"");

  arr[2] = 6;

  arr[4] = "猴子";

  document.writeln("arr的长度是:"+arr.length+"");

  先:他不包含任何元素,长度是0,之后在第三个和第五个元素赋值,所以该数组长度自动变成5;

  基本类型数组的初始化

  对基本类型数组而言,数组元素的值直接存储在对应的数组元素上。

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

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

  问题:引用变量何时只是栈内存中的变量本身,何时又变为引用实际的java对象?

  答:其实规则很简单,引用变量本质上只是一个指针,只要程序通过引用变量访问属性或方法,该引用变量将会由他所引用的对象代替。

  如下代码所示:

  //定义一个int[]类型的数组变量

  int[] iArr = null

  //只要不访问iArr的属性和方法,程序完全可以使用该数组变量

  System.out.println(iArr);

  //动态初始化数组

  iArr = new int[5];

  //只有当iArr指向有效的数组对象后,下面才可以访问iArr的属性

  System.out.println(iArr.length);

  引用类型数组的初始化

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

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

  使用数组:

  当数组引用变量指向一个有效的数组对象后,程序就可以通过该引用变量来访问数组对象。

  java语言不允许直接访问堆内存中的数据,因此无法直接访问堆内存中的数组对象,程序将通过数组引用变量来访问数组。

  一个极端的程序例子,方便理解数组在内存中的分配机制:

  public class ObjectArrayTest {

  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];

  //使objArr3和objArr2[1]指向同一个数组对象

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

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

  objArr3[1] = new int[5];

  //使iArr和objArr3[1]指向同一个数组对象

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

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

  for(int i=0;i

  iArr[i] = i*3+1;

  }

  //程序最精彩的展示,直接通过objArr访问iArr数组的第三个元素

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

  }

  }