• 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};
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    只有第一种动态初始化的方式,[] 中括号里面有数字。
    动态初始化:在创建数组时,直接指定数组中元素的个数
    静态初始化:在创建数组时不直接指定数据元素格式,而时将具体的数据内容进行指定
    故一个是数组个数指定(数组内容为默认值),一个是数组内容指定(数组个数为内容的个数)。
    静态初始化虽然没有指定数组的长度,但是编译器在编译时会根据{ }中元素的个数来确定数组的长度。,静态初始化可以简写,省去后面的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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    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下标?
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    在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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述
    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    调用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方法打印数组
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

        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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

        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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

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

    使用这个方法打印数组很方便,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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

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

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

    上述代码不叫拷贝,因为只有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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出结果:
    在这里插入图片描述
    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行结果:
    在这里插入图片描述
    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行结果:原来是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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

        public static native void arraycopy(Object src,  int  srcPos,
                                            Object dest, int destPos,
                                            int length);
    
    
    • 1
    • 2
    • 3
    • 4

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    部分拷贝:从源数组的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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果:
    在这里插入图片描述
    ④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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行结果: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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果:
    在这里插入图片描述
    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行结果:
    在这里插入图片描述
    分析:
    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

    可以看到是他帮我们用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
    • 2
    • 3
    • 4
    • 5

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

    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其中第三种方法: 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));
        }
    
    • 1
    • 2
    • 3
    • 4

    运行结果:
    在这里插入图片描述
    通过运行结果可以分析得到,如果省略那么值为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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    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));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

  • 相关阅读:
    HP E1740A 模拟量输入模块
    测开要做的开发工作到底是做什么
    CVE-2020-1957 Apache Shiro 认证绕过漏洞
    Python基础学习笔记3
    免费,C++蓝桥杯等级考试真题--第5级
    MySQL主从复制与读写分离
    营销复盘秘籍,6步法让你的活动效果翻倍
    NX二次开发UF_CAM_set_auto_blank 函数介绍
    Git --》Git与GitHub操作
    Shell别名的使用方法及管理技巧
  • 原文地址:https://blog.csdn.net/weixin_44070116/article/details/127672673