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

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

相关推荐
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸4 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象4 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了4 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
----云烟----4 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024065 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it5 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
懒洋洋大魔王5 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq