Java数组详解一
Java数组详解(一)
前言:
上节我们讲到了方法的,方法在Java中也是非常重要的内容,那么这期我将为带来Java中的数组的基础和使用的内容
一、创建数组及初始化
1.数组的创建
数组:我们可以将其看成为相同类型元素的一种集合
数组的定义有3种:
public class TestDemo {
public static void main(String[] args) {
//数组定义1
int[] array = {1,2,3,4};
//int[]是整型数组,array是数组变量(名)
//则下标为 0 1 2 3
//数组定义2
int[] array2 = new int[10];
//这种定义是属于定义了一个大小为10的整型数组
//上述初始化为动态初始化:通过new关键字
//在C语言中,数组通常会是随机值,但是在Java中,这么定义,数组中会是0;
//数组定义3
int[] array3 = new int[]{1,2,3,4,5,7};
//这种定义是指的是给数组进行赋值之后
//那么等号后面的int[]中的[]不能进行空间的大小的填写
//上述初始化为静态初始化:
//静态初始化虽然没有指定数组的长度,
//编译器在编译时会根据{}中元素个数来确定数组的长度。
//静态初始化时, {}中数据类型必须与[]前数据类型一致。
//静态初始化可以简写,省去后面的new T[]。T为数据类型
//实际中我们不用管那么多,直接写就可以了
//静态和动态初始化也可以分为两步,但是省略格式不可以
int[] array1;
array1 = new int[10];
int[] array4;
array4 = new int[]{10, 20, 30};
//但是我们不可以分两步直接进行赋值
//如:
int array5;
array5 = {1,2,3,4,5,6};
//这种会报错,只能在定义是同时赋值
System.out.println(array);
System.out.println(array2);
System.out.println(array3);
//打印出来是“地址”,但是它不是真正的地址,这是哈希值,
//Java太安全了,但是这个哈希值也是唯一的
}
}
" [ " 表示的是数组类型,“ I ”表示的是为整形;
这些地址是放在堆上,引用变量 –> 储存”地址“的变量。
2.数组的初始化
// 如果没有对数组进行初始化,数组中元素有其默认值
// 如果数组中存储元素类型为基本类型,默认值为基类类型对应的默认值,比如:
public static void main2(String[] args) {
char[] array1 = new char[10];
double[] array3 = new double[10];
float[] array4 = new float[10];
boolean[] array5 = new boolean[10];
//布尔初始化,值全为初始化为false
String[] string = new String[10];
//string类型数组初始化全为null
//string为引用类型
}
3.数组的使用
(1).数组中元素的访问
public static void main3(String[] args) {
int[]array = new int[]{1, 2, 3, 4};
array[0] = 100;//可以直接而对于某一项那个值去修改
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
//System.out.println(array[4]);
//数组没有第五个元素,所以下标4就代表越界访问
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
// at TestDemo.main(TestDemo.java:69)
// Disconnected from the target VM, address: '127.0.0.1:49482', transport: 'sock
//这个异常是代表数组越界访问的异常
System.out.println(array.length);
//C语言中得用sizeof来计算数组的长度,Java不用
//int sz = sizeof(array) / sizoef(array[0]);//数组的长度
//注意事项:
// 1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
// 2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
}
public static void main3(String[] args) {
int[]array = new int[]{1, 2, 3, 4};
array[0] = 100;//可以直接而对于某一项那个值去修改
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
}
这个时候就出现了异常:数组没有第五个元素,所以下标4就代表越界访问
这个异常代表数组越界访问
***注意事项:
数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常***
(2).数组的遍历
public static void main(String[] args) {
int[] array1 = new int[]{1,2,3,4,5,6,7,8};
//数组的遍历
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + " ");
}
System.out.println();//换行回车
//范围for语法for each
//这种语法我们拿不到数组元素的下标
for (int x: array1) {
System.out.print(x+" ");
}
System.out.println();
}
//对于下标的依赖,看情况和个人
二、数组的引用类型
引用类型:什么叫引用类型,在这个变量中,储存的是"地址”
JVM将内存划分几个区:
1.方法区,2.虚拟机区,3.本地方法栈区,4.堆,5.程序计数器
在Java SE 中我们需要重点了解堆和虚拟机区
平时所说的栈,其实指的就是Java虚拟机栈
如:局部变量存在栈里
堆:一般用来存储对象的
本地方法栈一般使用C语言和C++来写的
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),
堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
每一块内存都有自己的使命,堆和方法区是所有线程共享的数据区 本地方法栈和虚拟机区以及程序计数器是处于线程隔离区
public static void func() {
int[] array1 = new int[3];
array1[0] = 10;
array1[1] = 20;
array1[2] = 30;
int[] array2 = new int[]{1,2,3,4,5};
array2[0] = 100;
array2[1] = 200;
array1 = array2;
array1[2] = 300;
array1[3] = 400;
array2[4] = 500;
}
谈一下对这段代码的理解:
首先这个数组1,在main方法中,先指向了堆中的数组1的对象,分别是10,20,30;假设这个对象的地址是0x18,虚拟机上的main方法栈中保存了array1这些地址,array2定义为了1,2,3,4,5;这时候堆中array2的对象的值1,2,3,4,5;但是又对array2[0]和[1]修改,所以这时候堆中array2的对象的值变成了100,200,3,4,5;再通过了array1=array2赋值,假设array2的地址是0x78,所以虚拟机中的main方法栈中两者的保留的地址均是0x78.所以这个时候array2的对象中的值就会变成了100,200,300,400,500。所以这两个引用均同时指向了一个对象,所以不管通过哪个引用,都可以把数组对象当中的值进行修改,那么array1的对象10,20,30,没有人再指向它了,那么JVM会自动把它回收掉 ;当func方法执行完毕,此时array1和array2是局部变量,此时就会被回收,那么array2所指向的对象没有其它引用所指向的,所以它也会被JVM而自动回收掉
//null 在 Java 中表示 "空引用" , 也就是一个不指向对象的引用.
public static void main3(String[] args) {//null
//int[] array = 0;
//通常把数组直接赋值为0是错误的
//因为它指的是引用类型,而零是整数类型,两者是不同的
int[] array3 = null;
//array3这个引用不指向任何对象
System.out.println(array3.length);
//这个时候会发生报错,因为array3不指向任何数组对象,所以也就无法就计算长度
//这个时候就会出现空指针异常的报错提醒:NullPointerException
//这个异常会伴随你的一生在我们学习过程中
//如果以后遇到空指针异常那么我们需要定位,看看那个引用是空的
}
三、数组的应用
1.保存数据
public static void main(String[] args) {
int[] array = {1, 2, 3};
for (int i = 0; i < array.length; ++i) {
System.out.println(array[i] + " ");
}
}
2.作为函数的参数
2(1).参数传基本数据类型
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);
}
总结: 发现在func方法中修改形参 x 的值, 不影响实参的 num 值。
2(2).参数传数组类型(引用数据类型)
/**
* 这种是改变了形参的指向(引用)
* 在这种情况下,并不会影响到实参的指向
*/
public static void func1(int[] array) {
array = new int[10];//属于是对数组重新创建一个,这时候就会是改变了形参的指向
}
/**
* 这时候是array得到了array2的地址,
* 那么这个形参的array指向了栈上array2的对象,
* 所以这个时候改变了形参array的数值,地址一样,那么也就是两者指向了一个对象
* 所以对谁修改都会改变最后的对象的值
* @param array
*/
public static void func2(int[] array) {
array[0] = 99;
}
//总结: 所谓的 "引用" 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实
//只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
public static void main(String[] args) {
int[] array1 = {1,2,3,4};
func1(array1);
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + " ");
}
System.out.println();
int[] array2 = {1,2,3,4};
func2(array2);
for (int i = 0; i < array2.length; i++) {
System.out.print(array2[i] + " ");
}
System.out.println();
}
//总结: func1也就是改变了形参的指向(引用)
//当然,你不要以为传了引用,就万事大吉了,你要想想你拿引用干些什么了
func1在我原来的理解或许会打印为1,2,3,4,0,0···等
因为array1在main方法中,所以他会创建了array1,来保存它指向的地址,这个引用会指向array1的对象
但是当它进入到func1中,这时候也会在虚拟机的栈的func1方法上创建一个array(理解为载体),在array中存进的array1(是地址)指向的对象也没有发生变化,
但是这个时候func1我们会创建一个新的变量array(这个array的地址存进了那个载体中去),那么来储存array1(地址)的array(载体)的指向的对象就会发生变化,
那么它储存的地址也就会发生变化。所以最后的通过的func1的方法array1没有什么变化(其实变化也跟它没有相关的),所以最后是1,2,3,4.
其实一句话:就是改变了形参的指向,要不变,就是原来的,要不是原来的,那就是改变了指向的对象
说了这么多:我的理解,类比C语言的指针,array[数字],是对于同一块内存的中的内容发生改变,就是*p
array指向的改变,是地址的改变,即是从p = &a 改变到了 p = &b。
3.作为函数的返回值
/**
* 在这个方法中首先创建了一个数组,在堆上就会显示它的对象的值,此时通过引用获得了地址,这个地址存在了虚拟机栈上
* 再通过return,返回的array的地址
* @return
*/
public static int[] func3 (){
int[] array = new int[]{1,2,3,4,5,6};
return array;
}
public static void main(String[] args) {
int[] ret = func3();
//再通过ret接受func3所返回的array的地址,这时候通过地址指向了array的对象
for (int i = 0; i < ret.length; i++) {
System.out.print(ret[i] + " ");
}
//对象一定是在堆上,但是引用变量不一定是在栈上(后期会讲)
}
好了,这节我们先到这里,下一期博主将会为大家带来Java中的数组快速操作的方法及二维数组,当然,这节内容上如果有哪些地方的不足,欢迎大家在评论区指出!