数组本质上就是批量创建相同类型的变量
基本语法
//动态初始化
数据类型[] 数组名称 = new 数据类型[] { 初始化数据 };
//静态初始化
数据类型[] 数组名称 = { 初始化数据 };
Int[] arr = new int[]{1,2,3};
int[] arr = {1,2,3};
注意:静态初始化的时候,数组元素个数和初始化数据的格式是一致的
获取长度&访问元素
int[] arr = {1,2,3};
//获取数组长度
System.out.println(arr[1]); //执行结果:2
System.out.println(arr[0]); //执行结果:1
arr[2] = 100;
System.out.println(arr[2]); //执行结果:100
注意:
1.使用arr.length能够获取到数组的长度,.这个操作为成员访问操作符,后面在面向对象中会经常用到
2.使用[]按下标取数组元素,需要注意,下标从0开始计数
3.使用[]操作既能读取数据,也能修改数据
4.下标访问操作不能超出有效范围[0,length-1],如果超出有效范围,会出现下标越界异常
//下标越界
Int[] ar = {1,2,3};
System.out.println(arr[100]);
//执行结果
Exception in threan "main" javalang.ArrayIndexOutOfBoundsException:100
at Test.main(TEst.java:4)
抛出了javalang.ArrayIndexOutOfBoundsException异常,使用数组一定要下标谨防越界
遍历数组
"遍历"是指将数组中的所有元素都放问一遍,补充不漏,通常需要搭配循环语句
//遍历数组
int[] arr = {1,2,3};
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
//执行结果
1
2
3
//使用for-each遍历数组
Int[] ar = {1,2,3};
for(int x : arr){
System.out.println(x);
}
//执行结果
1
2
3
for-each是for循环的另外一种使用方式,能够个更方便的完成对数组的遍历,可以避免循环条件更新语句写错
public static void main(String[] args){
int[] arr = {1,2,3};
printArray(arr);
}
public static void printArray(int[] a){
for(int x : a){
System.out.println(x);
}
}
//执行结果
1
2
3
int[] a 是函数的形参,int[] arr 是函数的实参
如果需要获取到数组的长度,同样可以使用a.length
参数传内置类型
public static void main(String[] args){
int num = 0;
func(num);
System.out.println("num = " + num);
}
public static void func(int x){
x = 10;
System.out.println("x = "+ x);
}
//执行结果
x = 10
num = 0
修改形参x的值,并不能够影响实参num的值
参数传数组类型
public static void main(String[] args){
int[] arr = {1,2,3};
func(arr);
System.out.println("arr[0] = "+ arr[0]);
}
public static void func(int[] a){
a[0] = 10;
System.out.println("a[0] = "+ a[0]);
}
//执行结果
a[0] = 10
arr[0] = 10
在函数内部修改数组内容,函数外部也发生改变
此时数组名arr是一个"引用",当传参的时候,是按照引用传参
原因解释:
从内存说起
如何理解内存?
内存就是值我们熟悉的"内存",内存可以直观的理解成一个宿舍楼,有一个长长的大走廊,上面有很多房间
每个房间的大小是1 Byte(如果计算机有8G内存,则相当于有80亿个这样的房间)
每个房间上面又有一个门牌号,这个门牌号就称为地址
引用是什么?
引用相当于一个"别名",也可以理解成一个指针
创建一个引用只是相当于创建了一个很小的变量,这个变量保存了一个整数,这个整数表示内部那种的一个地址
针对 int[] arr = new int[]{1,2,3} 相当于又创建了一个int[] 变量。这个变量是一个引用类型,里面只保存了一个整数(数组的起始内存地址)
数组问题牵扯出一个新的问题关于栈与堆的认知
在前边未开始接触引用类型时,全部数据直接存进栈内,调用也直接在栈内进行操作,因此此前涉及的问题是,main方法中的局部变量,传入方法内调用时,如若其数值修改,且无返回值时,并不会影响主函数中变量的值,只是在方法中有所改变。而只有在有返回值的时候才会影响到主函数的值。
那么引进数组的概念,也就是引进了引用类型的实际使用
引用类型数据,存在堆中,而被存在栈中的mian与方法,都是通过地址的方式·访问该数据,也就是说,同一地址下的一个变量,只认最后一次修改后的值。//当然并不绝对只是作为一个记法
总结:所谓引用的本质只是存了一个地址,Java将数组设定成引用类型,这样的话后续进行数组参数传参,起始之时将数组的地址传入到函数形参中,这样可以避免对整个数组的拷贝(数组可能比较长,那么拷贝开销就会很大)
null在Java中表示"空引用",也就是一个无效的引用
int[] arr = null;
System.out.println(arr[0]);
//执行结果
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:6)
NullPointerException空指针异常,
null的作用类似于C语言中的NULL(空指针),都是标是一个无效的内存位置,因此不能对这个内存进行任何读写操作,一旦尝试读写,就会抛出空指针异常
注意:Java中并没有约定null和0号地址的内存有任何关联
程序计数器(PC Register):只是一个很小的空间保存下一条执行的指令的地址
虚拟机栈(JVM Stack):重点是存储局部变量表(当然也有其他信息),引用类型例如int[] arr 这样的存储地址的引用就是在这里保存
本地方法栈(Native Method Stack):本地方法栈与虚拟机栈的作用类似,只不过保存的内容是Native方法的局部变量,再有些版本的JVM实现中(例如HotSpot)本地方法栈和虚拟机栈是一起的
堆(Heap):JVM所管理的最大内存区域,使用new创建的对象都是在堆上保存(例如前面的new int[]{1,2,3,4})
方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,方法编译出的字节码就是保存在这个区域
运行时常量池(Runtime Constant Pool):是方法区的一部分,存放字面量(字符串常量)与符号引用(注意从JDK1.8开始,运行时常量池在堆上)
Native方法:
JVM是一个基于C++实现的程序,在Java程序执行过程中,本质上也需要调用C++提供的一些函数1进行和操作系统底层进行一些交互,因此在Java开发中也会调用到一些C++实现的函数
这里的Native方法就是指这些C++实现的,再由Java来调用的函数
在虚拟机中,程序计数器、虚拟机栈、本地方法栈被名叫Thread(线程)的方框圈起来了,并且存在很多份,而堆、方法区、运行时常量池只有一份
JVM栈
局部变量的引用保存在栈上,new出的对象保存在堆上
堆的空间非常大,栈的空间比较小
堆是整个JVM共享一个,而栈每个线程具有一份(一个Java程序中可能存在多个栈)
可变参数方法
其特殊的int...是指在传参的时候,不一定要传几个,然后传过来被存进数组array
public static int change(int ... array){
}
public static void main(String[] args){
change(1,2,3,4,5);
}
匿名数组