目录

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太安全了,但是这个哈希值也是唯一的
    }
}    

https://i-blog.csdnimg.cn/direct/4d6831373a0f4f2b882dc65e1644a62a.png

" [ " 表示的是数组类型,“ 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为元素个数,不能越界,否则会报出下标越界异常。
    }

https://i-blog.csdnimg.cn/direct/792ef22c301a45c6a1c6639f342a3936.png

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]);
}        

https://i-blog.csdnimg.cn/direct/6502f73ccf9e47cdad4bf20e8f4c52ee.png

这个时候就出现了异常:数组没有第五个元素,所以下标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();
}
    //对于下标的依赖看情况和个人

https://i-blog.csdnimg.cn/direct/65609d69e34042d691e676501238aba4.png

二、数组的引用类型

引用类型:什么叫引用类型,在这个变量中,储存的是"地址”

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);
}

https://i-blog.csdnimg.cn/direct/4aa2f58884904d34a9ad90c146585bbb.png

总结: 发现在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也就是改变了形参的指向(引用)
    //当然你不要以为传了引用就万事大吉了你要想想你拿引用干些什么了

https://i-blog.csdnimg.cn/direct/7336dbbc740048a5ba83d4b11a1f7478.png

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] + " ");
        }
        //对象一定是在堆上,但是引用变量不一定是在栈上(后期会讲)
	}

https://i-blog.csdnimg.cn/direct/079ec3bf41774814b7214fa5c9df721b.png

好了,这节我们先到这里,下一期博主将会为大家带来Java中的数组快速操作的方法及二维数组,当然,这节内容上如果有哪些地方的不足,欢迎大家在评论区指出!