《Java数组核心笔记:从遍历到内存原理全搞定》
数组的定义与使用(中)
遍历数组
所谓遍历是指将数组中的所有元素都访问一遍,访问是指对数组中的元素进行某种操作,比如打印:
…
int[]=newint[]{1,2,3};System.out.println(array[0]);System.out.println(array[1]);System.out.println(array[2]);…
上述代码确实起到了遍历的效果,不过如果数组增加一个元素就需要增加一个语句,有100个元素就需要写100个打印语句,并且如果我们把打印改成给每个元素加1,修改起来很麻烦。我们知道,打印数组的每个元素,对每个元素的操作是相同的,则可以采用循环进行打印:
…
int[]array=newint[]{1,2,3,4};for(inti=0;i<4;i++){System.out.println(array[i]);}…
那如果我们不知道数组中有多少个元素怎么办呢?
我们可以通过数组对象.length来获取数组的长度(数组中),改后代码如下:
…
int[]array=newint[]{1,2,3,4};for(inti=0;i<array.length;i++){System.out.println(array[i]);}…
使用循环解决了后面两个问题,不过依旧没有解决增加一个元素就得增加一条语句的不足;此时我们可以使用for-each遍历数组(for-each 是 for 循环的另外⼀种使⽤⽅式,能够更⽅便的完成对数组的遍历,可以避免循环条件跟循环语句写错),代码如下:
…
int[]array=newint[]{1,2,3,4};for(intx:array){System.out.print(x+" ");}…
这里的x是用来接受数组里每一个元素的临时变量,变量x可以换名字,但是变量x的类型必须与数组元素的类型一致,使用for-each没有下标的概念;不仅如此,还有一种方法打印数组元素更便利,不过是将数组转字符串,后面我会讲到(也就是操作数据工具类Arrays与数组)。
数组是引用类型
初始JVM(Java虚拟机)的内存分布
JVM:即Java虚拟机,是Java技术的核心,负责让Java实现”一次编写,到处运行”
编写的Java代码(也就是.java后缀文件)通过编译生成 .class 字节码文件,再通过JVM加载 .class 把字节码文件翻译成当前操作系统能执行的机器码
JDK = 开发工具(javac) + JRE
JRE = 核心类库 + JVM
内存是一段连续的存储空间,主要用来存储程序运行时的数据,比如:程序运行时代码需要加载到内存,程序运行长生的中间数据需要存放在内存,程序中的常量也要保存,有些数据需要长时间存储有些数据当运行结束后就要被销毁;因此JVM对所使用的内存按功能的不同进行了划分
虚拟机栈: 与⽅法调⽤相关的⼀些信息,每个⽅法在执⾏时,都会先创建⼀个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的⼀些信息,保存的都是与⽅法执⾏时相关的⼀些信息。⽐如:局部变量。当⽅法运⾏结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了
堆:JVM所管理的最大内存区域,使用new创建的对象都是在堆上保存,堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还在用就不会被销毁
方法区:用于储存虚拟机已被加载的信息、常量、静态变量、即时编译器编译后的代码等数据,方法编译出的字节码就是保存在这个区域
程序计数器:只是一个很小的空间,保存下一条执行的指令的地址
本地方法栈:本地⽅法栈与虚拟机栈的作⽤类似. 只不过保存的内容是Native⽅法的局部变量. 在有些版本的 JVM 实现中,本地⽅法栈和虚拟机栈是⼀起的(其实是C/C++代码实现的)
初学者只需简单了解堆和虚拟机栈,我们在方法内部创建的变量是局部变量,存储在虚拟机栈中,以数组为例:
…
int[]array=newint[]{1,2,3,4};…
array(引用变量)存储了数组对象的地址,引用指向数组对象
array是引用变量,存储在虚拟机里
new int[5] 是真正的数组对象,存储在堆里,这才是真正存放数据的地方
可以这么理解,引用变量是一张卡片,卡片里存了房子的地址(引用),地址(引用)指向房子(对象)。(引用就是对象的地址)
基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间存放的是其所对应的值;而引用数据类型创建的变量,一般称为对象的引用变量,其空间存储的是对象所在空间的地址;如下代码:
…
publicstaticvoidfunc(){inta=10;intb=20;int[]=newint[]{1,2,3,4};}…
在上述代码中,a、b、arr,都是函数内部的变量,因此其空间都在main⽅法对应的栈帧中分配。a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。array是数组类型的引⽤变量,其内部保存的内容可以简单理解成是数组在堆空间中的⾸地址,通过该地址,引用变量便可去操作对象。有点类似C语言的指针,但是Java中引用要比指针的操作更简单。用这个图可以更好的辨别:
认识null
null在Java中表示空引用,也就是一个不指向对象的引用,null的作用类似于C语言中的NULL(空指针),都是表示一个无效的内存位置,因此不能对这个内存进行任何读写操作,一旦尝试读写就会抛出 NullPointerException.(空指针异常)
数组的应用场景
保存数据
…
publicstaticvoidmain(String[]args){int[]array={10,20,30};for(inti:array){System.out.print(i+" ");}}…
作为方法的参数
参数传基本数据类型
…
publicstaticvoidmain(String[]args){intnum=100;fun(num);System.out.println(num);publicstaticvoidfun(inta){inta=200;System.out.println(a);}}//运行结果200100…
fun方法中修改形参num的值,不影响实参的num值
参数传数组类型(引用数据类型)
…
publicstaticvoidmain(String[]args){int[]array=newint[]{1,2,3,4};fun(array);System.out.println("a[0] = "+a[0]);publicstaticvoidfun(int[]array2){array[0]=100;System.out.println("a[0] = "+a[0]);}}//运行结果100100…
在方法内部修改数组的内容,方法外部的数组内容也发生改变,因为数组是引用类型,按照引用类型来进行传递,是可以修改其中存放的内容的
总而言之,所谓的引用本质上只是存了一个地址,Java将数组设定成引用类型,这样的话后续进行数组参数传参,其实只是将数组的地址传入到方法形参中,这样可以避免对整个数组进行拷贝(如果数组比较长,这样开销就会很大)。
对操作数据工具类Arrays与数组练习后面再讲解
