Java数组的定义与使用

  1. 数组的使命:存储一组相同数据类型的数据,数组可以看成是相同类型元素的一个集合,在内存中是一段连续的空间。
  2. 数组的创建和初始化
    有3种方法,动态初始化和静态初始化
    int[] arry = new int[N];
    其中int[] 表示数据类型, arry表示变量名/数组名
    public static void main(String[] args) {
        //定义和初始化数组的3种方法
        int[] arr1 = new int[10]; //只分配了内存,内容默认为10个0
        int[] arr2 = new int[]{1,2,3,4};
        int[] arr3 = {1,2,3,4};
    }

只有第一种动态初始化的方式,[] 中括号里面有数字。
动态初始化:在创建数组时,直接指定数组中元素的个数
静态初始化:在创建数组时不直接指定数据元素格式,而时将具体的数据内容进行指定
故一个是数组个数指定(数组内容为默认值),一个是数组内容指定(数组个数为内容的个数)。
静态初始化虽然没有指定数组的长度,但是编译器在编译时会根据{ }中元素的个数来确定数组的长度。,静态初始化可以简写,省去后面的new T[]。

  1. 我们常说,在Java中一切皆对象,Java当中的数组其实就是一个对象,一般通过new关键字实例化对象。
  2. 遍历数组的3种方法
    方法1:通过数组对象.length
    方法2:通过for each
    方法3:通过Arrays对象中的toString方法
    //实现一个方法 printArray, 以数组为参数, 循环访问数组中的每个元素, 打印每个元素的值.
    public static void printArray(int[] arr) {
        //方法1
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
        System.out.println();
        //方法2
        for (int x:
             arr) {
            System.out.print(x);
        }
        System.out.println();
        //方法3
        String ret = Arrays.toString(arr);
        System.out.println(ret);
    }
    public static void main(String[] args) {
        //创建的数组,并且赋初始值
        //创建一个 int 类型的数组, 元素个数为 10, 并把每个元素依次设置为 1 - 10
        int[] array = new int[10];
        for (int i = 0; i < array.length; i++) {
            array[i] = i + 1;
        }
//        for (int x: array) {
//            System.out.print(x + " ");
//        }
        printArray(array);
    }
}
  1. JVM的内存分布
    内存是一段连续的存储空间,主要用来存储程序运行时数据的,如果对内存中存储的数据不加区分的随意存储,那么对内存的管理会非常麻烦。
    JVM对所使用的内存按照功能的不同进行了划分:(5块区域)
    虚拟机栈、本地方法栈、、方法区、程序计数器

  2. 基本类型变量和引用类型变量的区别
    基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值。
    引用数据类型创建的变量,一般称为对象的引用,其空间中存放的是对象所在空间的地址(在堆中的地址)。
    局部变量的内存在栈上开辟,对象在堆上,引用变量其实就是一个变量来存地址。
    在这里插入图片描述
    引用变量并不直接存储对象本身,存储的是对象在堆中空间的起始地址,通过该地址,引用变量便可以去操作对象。

  3. Java中局部变量在使用的过程中必须初始化,如下a是局部变量,在a没有进行初始化的情况下输出打印a,会报错。
    在这里插入图片描述

  4. null在Java中表示空引用,不指向任何对象,表示一个无效的内存位置,不能对这个内存进行任何的读写操作,一旦尝试读写等操作(.length也不可以),就会抛出NullPointerException空指针异常。

public class Test {
    public static void main(String[] args) {
        int[] arr = null;
        int len = arr.length;
        int a = arr[0]; //不指向任何对象,哪来的0下标?

    }
}

在这里插入图片描述
在Java中没有约定null和0号地址的内存的关系,但是在C语言中null指0号地址(受保护的地址),注意Java中null就是代表引用不指向任何对象,不能进行任何读写操作。

  1. 两个引用同时指向一个对象,通过其中任何一个引用修改这个对象的值,另一个引用去访问那个值时,会发现也是被修改的。没有被引用的对象,会被系统自动回收,变成灰色,才C语言中要free释放,而在Java中是系统自动帮你回收。
    public static void main(String[] args) {
        int[] array = new int[]{1,2,3,4};
        int[] array2 = array;
        array2[1] = 100;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(array2));
    }

在这里插入图片描述
array和array2指向同一个对象,通过array2修改1号位置的值,array再次访问那个值是被修改之后的。

  1. 当数组作为参数进行传递的时候,其本质上还是值传递,只不过此时的值是一个地址,并且会出现2种情况
    ①形参修改指向新的对象,此时不会影响到实参
    ②形参指向实参的对象,并且修改对象的值,此时才会影响到实参。
    public static void fun1(int[] array) {
        array = new int[]{6,7,8};
    }
    public static void fun2(int[] array) {
        array[0] = 9;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4};
        fun1(array);
        //fun2(array);
        System.out.println(Arrays.toString(array));
    }

调用fun1运行结果
在这里插入图片描述
调用fun2运行结果
在这里插入图片描述
可以发现fun1并没有影响实参,而fun2影响了实参,具体的调用过程如图:
fun1:
在这里插入图片描述
fun2:
在这里插入图片描述
所谓的“引用”本质上只是存了一个地址,Java将数组设定成引用类型,这样的话后续进行数组参数传参,其实只是将数组的地址传入到了函数的形参中,这样可以避免堆整个数组的拷贝,如果数组可能比较长,那么拷贝开销就会很大。

  1. 数组也可以作为函数的返回值
    public static int[] fun10(){
        int[] array = {1,2,3,4};
        return array;
    }
    public static void main(String[] args) {
        //数组作为函数的返回值
        int[] ret = fun10();
        System.out.println(Arrays.toString(ret));
        //打印数组的很好用的方法,用Arrays类中toString方法打印数组
    }

在这里插入图片描述

    public static int[] grow(int[] array) {
        for (int i = 0; i < array.length; i++) {
            array[i] = array[i] * 2;
        }
        return array;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int[] array1 = grow(array);
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(array1));
    }

在这里插入图片描述
也可以改写成不要返回值,因为此时形参指向的数组对象和实参指向的是同一个。

    public static void grow(int[] array) {
        for (int i = 0; i < array.length; i++) {
            array[i] = array[i] * 2;
        }
//        return array;
    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
//        int[] array1 = grow(array);
        grow(array);
        System.out.println(Arrays.toString(array));
//        System.out.println(Arrays.toString(array1));
    }

在这里插入图片描述

  1. 数组转字符串 Arrays.toString()的实现
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        System.out.println(Arrays.toString(array));
    }

使用这个方法打印数组很方便,Java中提供了java.util.Arrays。
那么编写myToString实现 Arrays.toString()

    public static String myToString(int[] array) {
        if(array == null) {
            return null;
        }
        String s = "[";

        for (int i = 0; i < array.length; i++) {
            s += array[i];
            if (i != array.length - 1){
                s += ",";
            }
        }
        s += "]";
        return s;

    }
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int[] array2 = null;
        String ret = myToString(array);
        String ret2 = myToString(array2);
        System.out.println(ret);
        System.out.println(ret2);
        //System.out.println(Arrays.toString(array));
    }

在这里插入图片描述

注意得判断数组为null的情况,不然如果为null,使用.length会抛出空指针异常。

  1. 数组拷贝
    1.什么才叫拷贝?
    public static void main(String[] args) {
        int[] array = {1,2,3,4};
        int[] newArray = array;
    }

上述代码不叫拷贝,因为只有1个数组对象,两个引用指向的是同一个数组对象,并没有完成拷贝。
ctrl c —> ctrl v,有个一模一样的这叫拷贝。现在只是把他的值给了他而已,对象只有1个。
array和newArray引用的是同一个数组,因此此后通过newArray修改空间中内容之后,array也可以看到修改的结果。

2.数组拷贝的几种方法:①循环,②Arrays.copyOf(2个参数),③System.arraycopy(5个参数),④copyOfRange(3个参数),clone()。
①通过for循环进行拷贝

    public static void main(String[] args) {
        int[] array = {1,2,3,4};
        int[] array2 = new int[array.length];
        //不能初始化为null,否则后续的  array2[i]会报错,也不能不初始化,因为局部变量必须初始化
        for (int i = 0; i < array.length; i++) {
            array2[i] = array[i];
        }
        array2[0] = 199;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(array2));
    }

输出结果:
在这里插入图片描述
array2是一块独立的空间,改变array2不能影响array。
②通过Arrays调用copyOf进行拷贝

    public static void main(String[] args) {
        int[] array = {1,2,3,4};
        int[] array2 = Arrays.copyOf(array,array.length);
        array2[0] = 199;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(array2));
    }

运行结果:
在这里插入图片描述
copyOf扩容:第二个参数代表新的数组长度,可以通过其进行扩容:array.length*2,扩出来的为默认值

    public static void main(String[] args) {
        int[] array = {1,2,3,4};
        int[] array2 = Arrays.copyOf(array,array.length*2);
        array2[0] = 199;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(array2));
    }

运行结果:原来是4个,拷贝之后是8个,扩容了。
在这里插入图片描述
copyOf的源码:2个参数,第一个参数代表原始数组,第二个参数代表新的长度,返回值是一个数组

    public static int[] copyOf(int[] original, int newLength) {
        int[] copy = new int[newLength]; //先申请了一个长度
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

通过源码可以看见,copyOf是通过System.arraycopy方法来进行扩容的,即coyOf的底层用的System.arraycopy方法。
arraycopy方法的源码:这个方法连花括号都没有{},什么都没有,但前面有个native修饰

    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

native方法:叫做本地方法,特点:底层是由C/C++代码实现的(速度快),JVM实际上就是由C/C++代码写的,这个方法是看不到具体的实现的。
5个参数:src代表从哪里拷贝,srcPos从拷贝数组的哪个位置开始拷贝,dest:拷贝到哪里,destPos从拷贝数组拷贝到哪个位置,length拷贝的长度
③通过System.arraycopy实现拷贝,可以指定范围拷贝,支持部分拷贝,可以指定区间进行拷贝

    public static void main(String[] args) {
        //System.arraycopy
        int[] array = {1,2,3,5};
        int[] ret = new int[array.length];
        System.arraycopy(array,0,ret,0,array.length);
        System.out.println(Arrays.toString(ret));
        ret[0] = 199;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(ret));
    }

在这里插入图片描述
部分拷贝:从源数组的1号下标开始拷贝,此时拷贝的长度需要-1,否则就会出现数组越界异常。
在这里插入图片描述

    public static void main(String[] args) {
        //System.arraycopy
        int[] array = {1,2,3,5};
        int[] ret = new int[array.length];
        System.arraycopy(array,1,ret,0,array.length-1);
        System.out.println(Arrays.toString(ret));
        ret[0] = 199;
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(ret));
    }

运行结果:
在这里插入图片描述
④copyOfRange()实现部分拷贝,注意是左闭右开

    public static void main(String[] args) {
        //Arrays.copyOfRange()
        int[] array = {1,2,3,5};
        int[] ret = Arrays.copyOfRange(array,0,3);
        System.out.println(Arrays.toString(ret));
    }

运行结果:from 0 to 3 ,不包含3下标
在这里插入图片描述
⑤clone,数组名.clone
克隆:产生一个副本,注意是数组名点出来的

    public static void main(String[] args) {
        int[] array1 = {1,2,3,4};
        int[] array2 = array1.clone();
        System.out.println(Arrays.toString(array1));
        System.out.println(Arrays.toString(array2));
        array2[0] = 199;
        System.out.println(Arrays.toString(array1));
        System.out.println(Arrays.toString(array2));
    }

运行结果:
在这里插入图片描述
3.深拷贝和浅拷贝
深拷贝:array2[0]改变,不影响array1[0]的值
浅拷贝:array2[0]改变,影响array1[0]的值

深拷贝:
在这里插入图片描述
如果array2[0]的值改成99,array1[0]的值还是1不会改成99

浅拷贝:
在这里插入图片描述
在如上图的情况下,也可以实现深拷贝,比如array2不拷贝地址值,而是拷贝地址所指向的对象,这样就会形成新的地址,此时就是深拷贝了。(说明拷贝引用类型也可以实现深拷贝)
拷贝完成之后,修改拷贝对象的值,会不会影响原来对象的值?会影响就是浅拷贝,不会影响就是深拷贝。
深浅拷贝和是不是for循环、copyOf、clone拷贝无关,与代码的实现方式有关。

  1. Arrays的equals方法
    public static void main(String[] args) {
        int[] array1 = {1,2,3,4};
        int[] array2 = {1,2,3,4};
        System.out.println(array1 == array2);
        System.out.println(Arrays.equals(array1,array2));
    }

运行结果:
在这里插入图片描述
分析:
array1 和 array2 虽然值是一样的,但是这是2个不同的对象,在堆里有两块内存,例如一块存的0x11,一块存的0x15。所以不能通过等号来比较,而通过Arrays的equals方法来比较对象里面存的每个值一样不一样。

  1. Arrays的fill方法
    可以应用到动态初始化数组的赋值
    ①整体所有的赋值:
    Arrays.fill(2个参数),一个是要赋值的数组,一个是赋值的值是多少
    public static void main(String[] args) {
        int[] array = new int[10];
        //动态初始化,现在这10个位置的元素都是10
        //那么现在要把10个位置的元素都方成-1
        Arrays.fill(array,-1);
        System.out.println(Arrays.toString(array));
    }

运行结果:
在这里插入图片描述
Arrays.fill源代码:

    public static void fill(int[] a, int val) {
        for (int i = 0, len = a.length; i < len; i++)
            a[i] = val;
    }

可以看到是他帮我们用for循环写好了,然后我们直接调用就可以了。
②局部赋值:
多2个参数,即4个参数,from to
从1位置到3位置,左闭右开

    public static void main(String[] args) {
        int[] array = new int[10];
        Arrays.fill(array,1,3,-1);
        System.out.println(Arrays.toString(array));
    }

运行结果:
在这里插入图片描述

  1. 二维数组的定义(3种方式)
    public static void main(String[] args) {
        //二维数组的定义
        int[][] array1 = new int[][]{{1,2,3},{4,5,6}};
        int[][] array2 = {{1,2,3},{4,5,6}};
        int[][] array3 = new int[2][3]; //动态初始化,全为默认值
        System.out.println(Arrays.deepToString(array1));
        System.out.println(Arrays.deepToString(array2));
        System.out.println(Arrays.deepToString(array3));
    }

其中第三种方法: int[][] array3 = new int[2][3];,在C语言中定义二维数组可以省略行但是列不能省,在Java中可以省略列,但不能省略行。

    public static void main(String[] args) {
        int[][] array = new int[2][];
        System.out.println(Arrays.deepToString(array));
    }

运行结果:
在这里插入图片描述
通过运行结果可以分析得到,如果省略那么值为null

  1. 二维数组在内存中的存储
    在这里插入图片描述
    局部变量一定在栈上,array2是局部变量所以在栈上。二维数组是一个特殊的一维数组。
  2. 打印二维数组的三种方式
    public static void main(String[] args) {
        //打印二维数组的三种方式
        int[][] array = {{1,2,3},{4,5,6}};
        //1.for循环打印
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[i].length; j++) {
                System.out.print(array[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("============");
        //2.for-each打印
        for (int[] ret:array) {
            for (int x: ret) {
                System.out.print(x + " ");
            }
            System.out.println();
        }
        System.out.println("============");
        //3.调用Arrays里的deepToString方法
        System.out.println(Arrays.deepToString(array));
    }

运行结果:
在这里插入图片描述

  1. 不规则的二维数组
    Java中可以单独指定二维数组的列,从而可以造成不规则二维数组
    public static void main(String[] args) {
        //不规则二维数组
        int[][] array = new int[2][];
        array[0] = new int[]{1,2,3};
        array[1] = new int[]{6,7,8,9};
        System.out.println(Arrays.deepToString(array));
    }

运行结果:
在这里插入图片描述

分析:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeKnightShuai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值