数组、数组中的内存、参数传递、数组的工具类:Arrays

数组类型

数组:相同类型数据的集合,内存是连续的
一个数组只能储存一种数据类型,而不能储存多种数据类型。
Java中的数组既可以储存基本数据类型的数据,也可以储存引用数据类型的数据,只要所有的数据都具有相同的数据类型即可。
其实,数组也是一种数据类型,本身是一种引用类型。如:int是一种基本类型,int[]就是一种引用类型,可以使用int[]来定义变量,或者来进行类型转换

定义数组

有两种语法格式来定义数组:

type[] arrayName;	//是一种type[]类型的变量,变量名是arrayName
type arrayName[];	//相对于上一种,可读性相对较差

在上面的代码中,其实只是定义了一个引用变量(可以理解为指针),这个变量(指针)并没有指向任何有效的内存,所以这个数组是不能使用的,只有在初始化后才可以使用。

数组初始化

数组的初始化有两种方式:
静态初始化:初始化时由人为显式指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:只由人为指定数组长度,由系统为数组元素分配初始值。
静态初始化:

arrayName=new type[]{element1,element2,element3,.......};//type表示数组元素的数据类型

简化版本:

type[] arrayName={element1,element2,element3,.......};
例如:int[] array={1,2,3,4,5,6};

动态初始化:

arrayName= new type[length];//只指定数组的长度length,由系统分配初始值

分配初始值时与默认的初始值一样,即:
数组元素是整形(byte,short,int,long),数组元素值是0;
数组元素是浮点型(float,double),数组元素是0.0;
数组元素是字符类型(char),数组元素是’\u0000’;
数组元素是布尔类型(boolean),数组元素是false;
数组元素类型是引用类型,数组元素是null;

数组的使用

与C一样,都是通过引用下标来访问数组元素
需要记住一个异常信息:
java.lang.ArrayIndexOutOfBoundsException:N(数组索引越界访问异常),N就是越界的试图访问的数组索引。
所有的数组都提供一个length属性,通过这个属性可以访问到数组的长度

int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length);

数组的使用:

  1. 使用 arr.length 能够获取到数组的长度. 这个操作为成员访问操作符. 在面向对象中会经常用到.
  2. 使用 [ ] 按下标取数组元素. 需要注意, 下标从 0 开始计数
  3. 使用 [ ] 操作既能读取数据, 也能修改数据.
  4. 下标访问操作不能超出有效范围[0,length-1],超出会出现越界异常

遍历数组
两种方式;
for循环遍历

for(int i=0;i<array.length;i++){
	System.out.println(array[i]);//这样可以遍历数组元素
}

foreach遍历
使用foreach无需获得数组长度
foreach语法格式:

for(type variableName:array|collection)
{
	//variableName  自动迭代访问每个元素...
}
int[] array={1,2,3,4,5,6};
for(int x:array)
{
	System.out.println(x);
}

需要注意的是:使用foreach来迭代访问数组元素的值时,foreach中的循环变量相当于一个临时变量,系统把数组元素的值依次赋给这个临时变量,然后输出临时变量的值,而这个临时变量并不是数组元素,它只是保存了数组元素的值

深入数组——内存

数组是一种引用类型,数组元素和数组变量在内存中是分开存放的
数组引用变量是一个引用(指针),这个引用变量可以指向任何有效的内存。如果希望在程序中访问数组对象本身,只能通过这个数组的引用变量来访问它
实际的数组对象被储存在堆(heap)内存中;如果引用数组对象的数组引用变量是一个局部变量,那么它被储存在栈(stack)中
在这里插入图片描述
栈内存:当每一个方法执行时,每个方法都会建立自己的内存栈,这个方法定义的变量会逐个将变量放入栈中,随着方法的结束,这个方法的内存栈也会被销毁
如果堆内存中的数组没有任何引用指向它的话,这个堆内存中的数组将会成为垃圾,会被系统的垃圾回收器回收

参数传递机制

Java传递参数方式:按值传递

public static void swap(int a,int b)
{
	int tmp=a;
	a=b;
	b=tmp;
	System.out.println("swap方法中,a的值是"+a+";b的值是"+b);
}
//运行结果:swap方法中,a的值是9;b的值是6
public static void main(String[] args){
	int a=6;
	int b=9;
	swap(a,b);
	System.out.println("main中,a的值是"+a+";b的值是"+b);
}
//运行结果:main中,a的值是6;b的值是9

说明swap中的a和b并不是main中的a和b
在这里插入图片描述
在main中调用swap方法时,系统为main()和swap()方法分配两块栈区,用于保存局部变量。main中把a和b传给swap,实际上只是为swap栈中的a和b变量赋值6和9而已,后续的交换只是在交换swap栈区中的a和b而已,堆main中的a和b没有造成任何影响

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

而传递数组类型就堆main中的值产生了影响
在这里插入图片描述
传递数组名的方式,当在main中调用func方法时,传给func方法的参数实际是一个引用,就是把数组名传给了func方法,当开始执行func方法是,开辟一个栈区,此时func的变量a存放的是arr变量名(引用),这个引用指向堆区中的具体数组元素,当通过a[0]=10时,堆区中的下标为0的数据元素被修改为10,所以再通过main中的arr[0]访问时,数组元素被修改为10.

JVM内存区域划分

程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址.
虚拟机栈(JVM Stack): 重点是存储局部变量表(当然也有其他信息). 我们刚才创建的 int[] arr 这样的存储地址的引用就是在这里保存.
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量。在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的.
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.方法编译出的的字节码就是保存在这个区域.
运行时常量池(Runtime Constant Pool): 是方法区的一部分, 存放字面量与符号引用.
Native 方法:
JVM 是一个基于 C++ 实现的程序. 在 Java 程序执行过程中, 本质上也需要调用 C++ 提供的一些函数进行和操作
系统底层进行一些交互. 因此在 Java 开发中也会调用到一些 C++ 实现的函数.

native方法就是由C/C++实现,来由Java调用的函数

二维数组

二维数组实质上还是一维数组,只不过这个一维数组里存放的是引用而已,这个引用又指向一维数组
语法:

type[][] arrayName;

定义二维数组

数据类型[][] 数组名称=new 数据类型 [行数][列数]{初始化数据}

在这里插入图片描述
值得注意的是,二维数组并不是一定是规则的,即每行每列数据元素个数可以不相同

public static void main8(String[] args) {
        int[][] array5 = {{1},{3,4},{5,6}};

        System.out.println("长度1:"+array5.length);
        System.out.println("长度2:"+array5[0].length);
        System.out.println("长度3:"+array5[1].length);

        for (int i = 0; i < array5.length; i++) {
            for (int j = 0; j < array5[i].length; j++) {
                System.out.print(array5[i][j]+" ");
            }
            System.out.println();
        }

数组拷贝

1、for循环

public static int[] copyArray(int[] ori) {
        int[] ret = new int[ori.length];
        for (int i = 0; i < ori.length; i++) {
            ret[i] = ori[i];
        }
        return ret;
    }

2、System.arrayCopy();
System.arraycopy是System类提供的一个静态方法

public static native void arraycopy(Object src,  int  srcPos,
									Object dest, int destPos,int length);
//它是一个本地方法,所以效率比较高

src:源数组
srcPos:源数组要复制起始的位置
dest:目的数组
destPos: 目的数组放置的起始位置
length:源数组复制的长度
3、Arrays.copyOf();

int[] arr={1,2,3,4,6,3};
int[] array1=Arrays.copyOf(arr,2);//将arr数组的前2个数拷贝到array1数组中

System.arrayCopy()和Array.copyOf()区别?
从速度上来说,System.arrayCopy()比Array.copyOf()快
Arrays.copyOf(); 方法内部调用了System.arrayCopy();
System.arraycopy()是底层方法
Arrays.copyOf()是在方法中重新创建了一个数组,并调用System.arraycopy()进行拷贝。
浅拷贝和深拷贝:
深拷贝:即又创建了一个数组对象, 拷贝原有数组中的所有元素到新数组中,修改原数组, 不会影响到新数组
浅拷贝:如果数组当中存放的是引用类型,那么就是浅拷贝
如果是两个引用同时指向一个对象,那么通过一个引用,修改当前对象的值后,那么另一个引用也会受到影响,这种拷贝叫做浅拷贝

操作数组的工具类:Arrays

Arrays.toString(array);将array数组以字符串形式输出

int[] arr={123456789};
System.out.println(Arrays.toString(arr));
//输出[1, 2, 3, 4, 5, 6, 7, 8, 9]

Arrays.fill(array,9); 全部填充为9
Arrays.fill(array,2,7,9);2号下标到7号下标填充为9不包含7号下标

int[] array=new int[10];
System.out.printf(Arrays.toString(array));
//输出[0,0,0,0,0,0,0,0,0,0]
Arrays.fill(array,2,7,9);
System.out.printf(Arrays.toString(array));
//输出[0, 0, 9, 9, 9, 9, 9, 0, 0, 0]

Arrays.sort(array2);对数组进行排序

int[] arr={2,3,6,13,56,68,14,16};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//输出[2, 3, 6, 13, 14, 16, 56, 68]

值得注意的是这里系统采用的是快速排序,且是从小到大的排序
Arrays.deepToString(array);将二维数组以字符串形式输出

int[][] array={{1,2},{2,4},{3,6,8}};
System.out.println(Arrays.deepToString(array));
//输出[[1, 2], [2, 4], [3, 6, 8]]

注意:

int[][] array={{1,2},{2,4},{3,6,8}};
System.out.println(Arrays.toString(array));
//输出[[I@71e7a66b, [I@2ac1fdc4, [I@5f150435]

这里用toString打印二维数组,打印出来是一堆哈希码,这也说明了array所指向的一维数组确实存放的是引用,这个引用指向了二维数组的具体的数据元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值