前言:前面说过数组是一个引用数据类型,数组引用变量只是一个引用,数组元素和数组变量是分开存放的。正如定义数组的时候,只是定义一个引用变量,还需要再初始化。
一、内存中的数组
- 数组引用变量(就是一开始定义的)只是一个引用,它可以指向任何有效的内存(只要你给他初始化,说明它需要指向哪里)。换句话说,只有引用变量指向有效的内存空间,才可以通过该数组变量来访问数组元素(也就是内存里的内容)。
- 与所有引用变量相同的是,引用变量是访问真是对象的根本方式(如果想访问数组本身,只能通过这个数组的引用变量来访问它)。
关于数组的储存:实际的数组对像储存在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,则存储在栈(stack)内存中,如图:
也就是如果要访问图中的数组P,就只能通过P[index]的形式实现。关于堆内存和栈内存详情看https://www.cnblogs.com/SaraMoring/p/5687466.html
简单理解看下面:
关于栈内存和堆内存:
栈内存:用于一些基本类型的变量数据和对象的引用变量的存储;
堆内存:存放由new创建的对象和数组。
1 . 栈内存:当一个方法执行,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块内存栈中,随着方法的执行结束,这个内存栈也将自然销毁。也就是所有方法中定义的局部变量都放在栈内存中;
2 . 堆内存:在程序中创建一个对象时,将被保存在运行时数据区中,也就是堆内存中。堆内存中的对象不会随着方法的的结束而销毁,因为即使方法结束,该对象还可能会被另一个引用变量应用(方法的参数传递常见)。只有当这个对象没有被任何引用变量引用时,系统的垃圾回收才会在适合的时候回收它。
- 由堆内存可以知道,如果想让垃圾回收机制回收一个数组所占的内存空间,可以将数组的变量赋值为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);
for(int i=0,len=a.length;i<len;i++){
System.out.println(a[i]);
}
for(int i = 0,len = b.length;i<len;i++){
System.out.println(b[i]);
}
//因为a,b都是int[]类型,所以可以将a的值赋给b;
//也就是让b引用指向a引用引用指向的数组
b = a;
System.out.println("b的长度:"+ b.length);
}
}
结果:
b长度是:4
5
7
20
0
0
0
0
b的长度:3
示意图:
- 看待数组,一定要看成两部分:一个是定义的数组引用变量(例子中的a,b);另一个实际的数组对象(int[]),这一部分是在堆内存运行,通常无法直接使用,只能通过引用变量访问。
二、基本类型数组的初始化
int[] iArr;
iArr = new int[4];
for(int i = 0 ; i <iArr.length ; i++){
iArr[i] = i + 10;
}
过程图:
可以看到定义时,引用变量iArr并没有指向堆内存,因为没有初始化;
上图每个数组元素直接储存在对应的内存中,也就是说操作基本类型数组的数组元素时,实际上相当于操作基本类型的变量。
三、引用类型数组的初始化
- 引用类型数组的元素是引用,每个数组元素里存储的是引用,它指向另一块内存,这个内存里存储了有效数据。
//首先定义一个Person类(所有类都是引用类型,person类想当一个模子)
class Person{
public int age;
public double height;
//注意()
public void info()
{
System.out.println("我的年龄:"+age+"我的身高:"+height);
}
}
//下面将定义一个Person[]数组,只不过这个数组引用变量名字不是Person,而是student,并为这个数组的每个元素指定值(这个值就是实例化的Person)
public class ReferenceArrayTest{
public static void main(String[] args){
//定义一个student数组变量,类型是Person[]
Person[] students;
students = new Person[2];
//创建person实例,并将这个实例赋给zhang变量
Person zhang = new Person();
zhang.age = 15;
zhang.height = 158;
//创建person实例,并将这个实例赋给lee变量
Person lee = new Person();
lee.age = 23;
lee.height = 180;
//下面开始将zhang、lee的值赋给数组元素
students[0] = zhang;
students[1] = lee;
//下面两行代码结果一样
lee.info();
students[1].info();
}
}
结果:
我的年龄:23我的身高:180.0
我的年龄:23我的身高:180.0
过程图:
- 由图可以看出,最后两行代码结果一样,因为zhang和students[1]变量引用指向同一个内存区。如果修改students[1]所指向的person实例的实例变量,或者是zhang的,所修改的都是同一个内存变量,都会互相影响。
四、没有多维数组
java语言支持多维数组的语法,但如果从数组底层的运行机制来看——没有多维数组;Java语言里数组类型是引用类型,所以数组变量是一个引用,如果数组元素也是引用,指向真实内存,这种形式看起来像多维数组。
public class TwoDimesionTest{
public static void main(String[] args)
{
//定义一个二维数组,可以看成类型为int[]的一维数组
int[][] a;
//把a当成一维数组,初始化长度是4
//a数组元素又是引用类型,也就相当于4个一维数组
a = new int[4][];
//当成一维数组,遍历
for(int i = 0 , len = a.length; i < len ; i++)
{
System.out.println(a[i]);
}
//初始化数组第一个元素
//理解:上面只是动态初始化了整个a数组
a[0] = new int[2];
//访问第一个元素所指的第二个元素
a[0][1] = 6;
for(int i = 0 , len = a[0].length; i < len ; i++)
{
System.out.println(a[0][i]);
}
}
}
结果:
null
null
null
null
0
6
过程图:
注意:图中a[0]的元素,不能再指向另一个数组。因为Java是强类型语言,前面已经定义了数组元素是int[]类型,所以a[0]中的数组元素只能是int类型。
想实现无限扩展的数组,可以定义一个Object[]类型的数组。
//一、动态初始化另一种写法:同时初始化二维数组的两个维数
int[][] b = new int[3][4];
//二、静态方法来初始化,指定多个一维数组作为二维数组的初始化值
String[][] str1 = new String[]{new String[3],new String[]{"Hello!"}};
//更简化
String[][] str2 = {new String[3],new String[]{"Hello!"}};
五、Java 8增强的工具类:Arrays
Java提供Arrays类里包含一些static修饰的方法可以直接操作数组,static修饰方法(通过类名调用),这里暂时不写。
//Arrays处于java.util包下,需要在代码中导入,这里省略
int[] a = new int[]{3,4,5,6};
int[] a2 = new[]{3,4,5,6};
//a数组与a2长度相等,每个元素依次相等,将输出true
System.out.println(Arrays.equals(a,a2));
//复制a数组,得到一个新数组b
int[] b = Arrays.copyOf(a,6);
System.out.println(Arrays.equals(a,b));
//输出{3,4,5,6,0,0}
System.out.println(Arrays.toString(b));
//将b数组的包括第3个元素到第5个元素不包括,赋值为1
Arrays.fill(b,2,4,1);
//输出{3,4,1,1,0,0}
System.out.println(Arrays.toString(b));
///排序
Arrays.sort(b);
System.out.println(Arrays.toString(b));
- Java 8支持并发
下面代码涉及接口,内部类,简单理解,以后回头再理解
int[] arr1 = new int[]{3,-4,25,16,30,18};
Arrays.parallelSort(arr1);
System.out.println(Arrays.toString(arr1));
ing[] arr2 = new int[]{3,-4,25,16,30,18};
Arrays.parallelPrefix(arr2,new IntBinaryOperator)(){
public int applyAsInt(int left,int right)
{
return left*right;
}
}