Java(二)——方法与数组

文章目录

方法与数组

方法

什么是方法?

方法就是一个代码片段,Java的方法类似于C语言的函数,方法的意义在于:

  • 能够模块化地组织代码
  • 能偶做到代码被重复使用,而不需要重新写一份,一份代码可以在多个位置使用
  • 便于理解代码,增加代码可读性

方法的定义

方法的语法格式

复制代码
修饰符 返回值类型 方法名称(参数类型 形参......){
  方法具体代码;
  [return 返回值];
}

与C语言对比,Java的方法定义多了修饰符,且代码风格规定花括号不换行。

我们实现一个简单的两个整数的加法方法:

java 复制代码
public static int add(int x, int y){
    return x + y;
}

注意:

  • Java学习初期,修饰符固定写public static即可,这部分知识从类和对象部分开始
  • 返回值类型必须与返回的实体类型一致,如果没有返回值,返回类型处写成void
  • 方法命名建议采用小驼峰命名方式,即从第二个单词开始,首字母大写,例如sumOfScore
  • 在Java中方法必须写在类中
  • Java中方法不能嵌套定义
  • Java中没有方法声明一说,如果将方法的定义写在了调用前,也是没问题的

方法的执行

方法的调用过程:

调用方法------>传递参数------>找到方法地址------>执行被调方法的方法体------>被调方法结束返回------>回到主调方法继续往下执行


实参与形参

与C语言一样,形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。

在Java中,实参永远都是拷贝到形参中,形参和实参本质是两个实体

以交换两数为例:

java 复制代码
    public static void swap(int x, int y){
        int tmp = x;
        x = y;
        y = tmp;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        System.out.println("交换前" + "a:" + a + " " + "b:" + b);
        swap(a, b);
        System.out.println("交换后" + "a:" + a + " " + "b:" + b);
    }

【分析】

实参a和b是main方法中的两个变量,其空间在main方法的栈中,而形参x和y是swap方法中的两个变量,x和y的空间在swap方法运行时的栈中,因此:a和b与x和y是没有任何关联的。

在swap方法调用时,只是将实参a和b中的值拷贝了一份传递给了形参x和y,所以对形参x和y的操作对实参a和b没有影响。

对于基础类型来说,形参相当于实参的拷贝,即传值调用。那么我们怎么使用方法来交换两个变量的值呢?

引用类型参数(例如数组):

java 复制代码
    public static void swap(int[] array){
        int tmp = array[0];
        array[0] = array[1];
        array[1] = tmp;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] array = new int[2];
        array[0] = scanner.nextInt();
        array[1] = scanner.nextInt();
        System.out.println("交换前:" + array[0] + " " + array[1]);
        swap(array);
        System.out.println("交换前:" + array[0] + " " + array[1]);
    }

方法重载

用一种情景引入:

java 复制代码
public static int add(int x, int y) {
    return x + y;
}

如上,我们实现了一个整数加法方法。如果我们这时候想求两个double类型数据的和,上面的方法不适用了,我们该怎么办?

首先想到的是再实现一个方法,针对double类型,这确实可以解决问题,麻烦的一点是,我们需要提供许多不同的方法名,调用时还需要考虑调用哪个,而这些方法的逻辑都是相加。针对这个问题,Java提供了方法重载 ,允许将上述方法名都写成同一个,例如add

方法重载的概念

在Java中,如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。

【代码示例】

java 复制代码
    public static int add(int x, int y) {
        return x + y;
    }
    
    public static double add(double x, double y){
        return x + y;
    }
    
    public static int add(int x, int y, int z){
        return x + y + z;
    }
    public static void main(String[] args) {
        System.out.println(add(10, 20));
        System.out.println(add(1.5, 2.5));
        System.out.println(add(10, 20, 30));
    }

没有报错,结果也没有问题。

在IDEA上也会有相应的提示。

什么情况下叫方法的重载?

  • 方法名一样
  • 参数列表不一样,参数的【个数、类型、顺序】
  • 与返回值无关(也就是说,只有返回值不一样,两个方法不能称为构成重载)
  • 这样的若干个方法互为方法的重载

编译器在编译代码时,会对实参类型进行推演,根据推演的结果来确定调用哪个方法。


方法签名

在同一个作用域中不能定义两个相同的标识符。那么为什么类中就可以定义方法名相同的方法呢?

因为有方法签名 ,重载方法的函数名虽然相同,但是它们的方法签名不同。

方法签名即,经过编译器编译修改后的最终的名字。完整名字包含:

方法全路径名 + 参数列表 + 返回值类型

我们可以使用JDK自带的javap反汇编工具查看,具体操作:

  1. 在源代码目录打开cmd
  2. 使用javac编译生成.class文件,如果源代码有汉字,那么要统一编码
  3. 输入:javap -v 字节码文件名

完成后寻找签名:

  • work是类名

  • add是方法名

  • 括号里的内容代表方法的参数列表信息

  • 括号后面代表返回值信息

  • 如图,I表示int类型,D表示double类型,更多的信息如下:


数组

学过C语言的小伙伴应该对数组了解不少也使用了很多吧,接下来我们介绍一下Java中的数组。

创建与初始化

数组的创建

C语言中,创建一个数组通常这么写:

c 复制代码
int array[10];

C语言形式有以下特点:

  • 数组名在方括号的左边
  • 如果不初始化,方括号中必须给定数组大小,且必须是整型常量

array是数组名,类型认为是int[10],这与我们创建变量的规则类型 + 变量名不符。所以,Java不倾向于这样写。

Java创建一个数组有以下常见写法:(注意事项均在注释中给出)

java 复制代码
int array1[];//与C语言的风格类似,不过Java中的[]中不能写数字,要是空的,否则报错
int[] array2;//第三种的简写版,符合 类型+变量名 的习惯,[]中仍然不能写数字,否则报错
int[] array3 = new int[10];//看起来最难懂的,是第二种的完整版,前面的[]必须为空,后面的[]中不能为空,且这种写法不能直接花括号初始化,具体怎么初始化见数组的初始化的内容。

数组的初始化

常见的初始化方式有三种:

java 复制代码
int array1[] = {1, 2, 3};  
int[] array2 = {1, 2, 3};   
int[] array3 = new int[]{1, 2, 3};
  • 前两种初始化都是基于创建的前两种进行初始化的
  • 第三种初始化与上面第三种创建方式相似,需要注意的是:
    1. 第二个[]必须为空
    2. 第二个方括号后不需要加=,直接花括号初始化即可

了解到这,我们看一下第三种的错误写法,这里很易混

java 复制代码
int array[] = new int[3] = {1, 2, 3};//ERROR,错误的,第二个方括号必须是空
int array[] = new int[];//ERROR,错误的,没有初始化,第二个方括号不能为空

如果我们没有对数组进行初始化,数组中的元素有其默认值:

  • 如果数组中存储的是基本类型,默认值如下表:

  • 如果数组中元素存储的是引用类型,默认值是null

不过,在Java中也允许使用C语言的书写方式,这并不会报错

数组的使用

【数组元素的访问】

与C语言一样,Java中的数组是一块连续的空间,空间的编号都是从0开始,该编号称为数组的下标,通过下标就可以访问数组元素了,所以对于有5个元素的数组,它的元素下标从0 ~ 4。

java 复制代码
public static void main(String[] args) {
    int[] array = new int[]{1, 2, 3, 4, 5};
    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]);
}

【数组的遍历】

我们介绍三种数组遍历的方式,它们均能打印出所有的数组元素。

java 复制代码
int[] array = new int[]{1, 2, 3, 4, 5};

//1.for循环遍历   
for (int i = 0; i < array.length; i++) {  
    System.out.print(array[i] + " ");
}

//2.foreach循环遍历,增强型for循环
for (int x:array) {   
    System.out.print(x + " ");
}

//3.导入包,使用 Arrays.toString
import java.util.Arrays;
System.out.println(Arrays.toString(array));

我们看到三种方式都将数组元素打印出来了。

  • 我们使用for循环打印时,要考虑循环次数,就是数组容量大小,Java中不存在C语言的sizeof,Java中通过数组对象.length获取数组长度,如上面的代码所示。

  • foreach更方便,不过功能没有for循环强大。for后面的括号中有一个,它的右边是集合的名称,左边是集合元素类型+变量名(自定义的)。不过,在需要使用到下标(索引)的情景下,foreach循环就无能为力了。

  • 第三种方式是将数组元素转换成字符串类型,然后进行打印,打印的格式是固定如此的。

数组的类型

数组是引用类型,引用类型不直接存储数据值,而是存储对数据的引用的类型。

说到引用类型,不得不先了解JVM的内存划分:

我们发现这与C语言的内存划分不同。

我们这里仅理解 运行时数据区虚拟机栈 即可。

  • 虚拟机栈就是我们C语言学习过程中常说的栈:

    存储与方法调用相关的信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含:局部变量表、操作数栈、动态链接、返回地址等其他信息。比如局部变量。当方法运行结束后,即栈帧中保存的数据也会被销毁

  • 与C语言的堆不同,C语言动态开辟的内存都是在堆上,而Java的堆:

    JVM所管理的最大内存区域,使用new创建的对象都是在堆上保存,堆是随着程序开始运行而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁

引用数据类型创建的变量,一般称作对象的引用,其空间中存储的是对象所在空间的地址。

我们观察一个代码:

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[]{1, 2, 3, 4, 5};
        System.out.println(array);
    }

打印结果如下:

对于这个打印结果:

  • [表示 引用变量array指向的是一个数组对象
  • I表示其指向的数组对象的每个元素都是int类型
  • @是分割符
  • 1b6d3586可以理解为 "地址"

看下面段代码:

java 复制代码
    public static void Test(){
        int a = 10;
        int b = 20;
        int[] array = new int[]{1, 2, 3, 4, 5}
    }

调用Test函数时,会为Test方法在栈上创建一个栈帧,Test栈帧内创建了局部变量abarray,由于ab都是基本类型,所以在栈区存储的就是其数据,array是引用类型,所以它存储的是指向数组对象的"地址",引用变量array创建时,同时会在堆区开辟一块空间,这块空间存储数组对象的数据。array变量存储的"地址"指向堆上指定位置的数组对象,我们常说:"引用指向了对象"。

再看一段代码:

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[]{6, 7, 8, 9, 0};
        array1 = array2;
    }

这段代码中array1 = array2的效果是什么?这是引用变量的相互赋值。

效果是:array1引用指向了array2引用指向的对象。

我们通过代码验证一下:

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[]{6, 7, 8, 9, 0};
        array1 = array2;
        //赋值后打印验证
        System.out.println(Arrays.toString(array1));
        System.out.println(Arrays.toString(array2));
        System.out.println(array1);
        System.out.println(array2);
    }

引用指向对象,我们可以将一个引用的值赋给另一个引用,就是 "一个引用指向了另一个引用指向的对象",这时候,其中一个对象没有引用指向它了,Java会自动检测没有引用指向的对象,它在堆上的内存会被自动回收。

空引用

null就是空引用,我们可以给引用变量初始化为空引用。

null就是不指向任何对象的引用,与C语言的NULL类似,都是指一块无效的内存地址。

数组应用

我们这里介绍的是Java内部实现好的功能(类的成员方法),对于数组的这些功能,我们导入包中的Arrays类,便可以直接使用了。

转字符串

介绍遍历数组时我们已经介绍了这一应用:

Arrays.toString()

传入数组即可。

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[]{1, 2, 3, 4, 5};
        System.out.println(Arrays.toString(array));
    }

验证转换后的结果是String类型,我们用一个String类型的变量接收,不会报错。

排序

Arrays.sort()

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
        System.out.println("排序前:");
        System.out.println(Arrays.toString(array));
        System.out.println("排序后:");
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
  • 此方法默认排升序
查找(二分)

Arrays.binarySearch()

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[]{1, 2, 3, 4, 5};
        int ret = Arrays.binarySearch(array, 2);
        System.out.println("查找后的下标为: " + ret);
    }

当然,Java中还重载了很多的有关二分查找的类方法,例如:不同类型数据的查找、指定范围内查找

我们再介绍一下指定范围内查找的方法:

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[]{1, 2, 3, 4, 5};
        int ret = Arrays.binarySearch(array, 0, 2, 3);
        System.out.println("在下标0~1之间查找:" + ret);
    }

这里我们介绍两点:

  • 指定范围内查找,参数有4个,分别是数组、开始点、结束点、待查找值

    值得注意的是,按指定区间查找都是 [) 的, 如果我们想在下标为1~3的区间查找,我们传入的起始值分别要是:1、4

  • 为什么返回值是-3?

    首先-3肯定代表查找失败,此方法内部实现的查找失败的返回值是:return -(low + 1)low就是我们自己实现二分的left

填充

Arrays.fill()

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[10];
        System.out.println("填充前: " + Arrays.toString(array));
        Arrays.fill(array, 5);
        System.out.println("填充后: " + Arrays.toString(array));
    }

当然,我们也可以在指定范围内填充,传入的起始点也是遵循[)

java 复制代码
    public static void main(String[] args) {
        int[] array = new int[10];
        System.out.println("填充前: " + Arrays.toString(array));
        Arrays.fill(array, 1, 5, 5);
        System.out.println("填充后: " + Arrays.toString(array));
    }
拷贝

Arrays.copyOf()

这个类的方法有两个作用:1. 拷贝 2. 扩容

另外,指定范围内拷贝: Arrays.copyOfRange()

【拷贝功能】

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[5];
        System.out.println("拷贝前:" + Arrays.toString(array2));
        array2 = Arrays.copyOf(array1, 5);
        System.out.println("拷贝后:" + Arrays.toString(array2));
    }

【扩容功能】

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[5];
        System.out.println("扩容前:" + Arrays.toString(array2));
        array2 = Arrays.copyOf(array1, 10);
        System.out.println("扩容后:" + Arrays.toString(array2));
    }

【指定范围内拷贝】

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[5];
        System.out.println("拷贝前:" + Arrays.toString(array2));
        array2 = Arrays.copyOfRange(array1, 1, 3);
        System.out.println("拷贝下标1~2的元素后:" + Arrays.toString(array2));
    }

此外,还有一种数组拷贝的方法:

System.arraycopy()

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[5];
        System.out.println("拷贝前:" + Arrays.toString(array2));
        
        System.arraycopy(array1, 0, array2, 0, 3);
        
        System.out.println("拷贝后:" + Arrays.toString(array2));
    }

System.arraycopy(array1, 0, array2, 0, 3);语句的含义是:从array1数组的 0 下标位置开始拷贝 3 个元素到 array2数组,从 0 位置开始。

  • 四个参数 :待拷贝数组、拷贝起始位置、待存放数组、存放的起始位置、拷贝的元素个数
判等

Arrays.equal()

java 复制代码
    public static void main(String[] args) {
        int[] array1 = new int[]{1, 2, 3, 4, 5};
        int[] array2 = new int[]{1, 2, 3, 4, 5};
        System.out.println(Arrays.equals(array1, array2));
    }

我们判断两个数组相等不能简单的使用==实现,因为数组名存放的是"地址"。

我们可以调用这样一个方法实现判等操作。

二维数组

二维数组本质上就是一维数组的数组,其每一个元素都是一维数组

创建及初始化
java 复制代码
//创建方法一 :规则与一维数组一样,后两个[]内必须有内容,不能为空
数据类型[][] 数据类型 = new 数据类型[行数][列数];//默认都是0

//创建方法一并初始化 :此时所有的[]都必须为空
数据类型[][] 数组名称 = new 数据类型[][]{初始化};

//创建方法二 :第一种的简写
数据类型[][] 数组名称;

//创建方法二并创建 :
数据类型[][] 数组名称 = {初始化};

我们继续将Java与C语言对比:

  • 语句开始的两个[]中不能添加内容

  • C语言中定义二维数组时可以省略行数,而Java中可以省略列数。

    这里针对的是第一种创建方式并初始化的情况,后两个[],这里有一个知识点,就是不规则数组

  • 由于C语言不可省略列数,所以初始化时可以在{}中直接罗列数据,C语言会帮我们区分行列;而Java中不允许,必须在{}内部再加{}来手动区分行数和列数

遍历

双循环遍历即可打印

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 2; j++) {
                System.out.print(array[i][j] + " ");
            }
            System.out.println();
        }
    }

利用Arrays.deepToString()

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};
        System.out.println(Arrays.deepToString(array));
    }

foreach循环

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};
        for (int[] arraytmp : array) {
            for (int data : arraytmp) {
                System.out.print(data + " ");
            }
            System.out.println();
        }
    }

现在有两个问题:

  1. foreach遍历虽然不需要行数和列数,但是它是有局限性的。如果我们采用第一种方式遍历,我们怎么拿到二维数组的行数和列数呢?每次都要自己数吗?

    当然不用,有办法,就像遍历一维数组时的array.length

2.deepToString能遍历我了解了,前面一维数组介绍的toString不行吗?

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};
        System.out.println(Arrays.toString(array));
    }

想要真正理解上面的问题,我们得了解二维数组的本质和内存分布

本质和内存分布

二维数组就是一个特殊的一维数组,存储的元素是一维数组的"地址",前面toString打印结果我们都已经见过了。

每一个"地址"都能找到特定的一维数组。

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};
        System.out.println(array);
        System.out.println(Arrays.toString(array));
    }

对于第一行打印:

  • [[指二维数组
  • I指每个元素是int类型
  • @是分隔符
  • 后面的是"地址"
  • 所以,二维数组也是一个引用变量,指向了一个数组对象,数组中存放的是引用变量,数组元素数量是行数,每个引用变量指向了一个数组对象,数组中存放的是int类型

根据上述,我们可以画出内存分布图:

图中的地址都是随便给出的,这不重要。

了解到这,我们可以写出第一种遍历方式的通写形式了:

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 2}, {3, 4}, {5, 6}};

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

外层循环使用array.length就是行数,内层循环使用array[i].length是列数

不规则二维数组

我们知道,Java中的二维数组的列数可以省略,行数不可以省略,例如:

java 复制代码
int[][] array = new int[2][];//不清楚列数

这时候,我们尝试遍历:

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[2][];
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[i].length; j++) {
                System.out.println(array[i][j] + " ");
            }
            System.out.println();
        }
    }

抛出了"空指针异常"(Java中没有指针,习惯这么讲),此时没有初始化列:

我们如果想要使用,必须对列初始化,这就能体现出不规则了。

根据上图,二维数组的每个元素(引用类型)为null,说明不指向任何对象。

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[2][];
        array[0] = new int[3];
        array[1] = new int[5];
        System.out.println(Arrays.deepToString(array));
    }

每一行的列数不必相等,这就体现了不规则

进行到这,我们二维数组的两个元素指向了各自的数组对象,此时可以打印了:

其实,我们也可以通过初始化数组体现不规则:

java 复制代码
    public static void main(String[] args) {
        int[][] array = new int[][]{{1, 3}, {5, 6, 7}};
        System.out.println(Arrays.deepToString(array));
    }

以上就是我们要介绍的内容了,小裤也是边学边总结,如果有问题,希望大家帮忙指正。

相关推荐
0白露1 小时前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.2 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
战族狼魂2 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
Tttian6223 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch4 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
独好紫罗兰4 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法
天天向上杰5 小时前
面基JavaEE银行金融业务逻辑层处理金融数据类型BigDecimal
java·bigdecimal