小白Java笔记——深入数组

前言:前面说过数组是一个引用数据类型,数组引用变量只是一个引用,数组元素和数组变量是分开存放的。正如定义数组的时候,只是定义一个引用变量,还需要再初始化。

一、内存中的数组

  • 数组引用变量(就是一开始定义的)只是一个引用,它可以指向任何有效的内存(只要你给他初始化,说明它需要指向哪里)。换句话说,只有引用变量指向有效的内存空间,才可以通过该数组变量来访问数组元素(也就是内存里的内容)。
  • 与所有引用变量相同的是,引用变量是访问真是对象的根本方式(如果想访问数组本身,只能通过这个数组的引用变量来访问它)。
  • 关于数组的储存:实际的数组对像储存在堆(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;
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值