java方法和数组介绍

前言:大家好,我是笨鸟先飞,目前是一名在校大学生。本篇介绍java语法的基础部分

本篇文章偏基础适合在学java的小白🤭🤭。

如果文章有什么错误,请各位佬指教😭😭。

在此先感谢大家百忙之间抽出时间来看我写的博客。

个人主页:笨鸟先飞

系列专栏:java从0开始

网站推荐:面试前刷题,学习时刷题,新手都可以使用这个牛客网在有一定基础后可以使用力扣

1.举个例子

我们在生活中需要问题时,可能需要向老师或者辅导员请教,在你问这个问题老师给你讲解了过后,过了一会儿张三又去问这个问题,于是老师给张三又讲了一遍了张三懂了,但是李四不懂啊!于是李四又去问老师了,后来老师不单独讲了,他在黑板又写了一遍,有那个同学不懂了就再去看一下。

在这里插入图片描述

这个在黑板写的一遍我们可以理解为我们定义的方法,同学不懂的去看(java里我们叫调用)就可以了

这个方法其实类似于C语言中的函数,大家可以类比学习一下!

在编程中其实也一样,某段代码的功能可能会被频繁的使用,如果在每个位置都重新实现一遍,会:

1.使程序变得繁琐
2. 开发效率低下,做了大量重复性的工作
3. 不利于维护,需要改动时,所有用到该段代码的位置都需要修改
4. 不利于复用

因此,在编程中,我们也可以将频繁使用的代码封装成"黑板"(方法),需要时直接看(即方法名–方法的入口 地址)使用即可,避免了一遍一遍的累赘。

image-20221104171112205

2.方法概念及使用

2.1什么是方法?

方法就是一个代码片段. 类似于 C 语言中的 “函数”。方法存在的意义(不要背, 重在体会):

  1. 是能够模块化的组织代码(当代码规模比较复杂的时候).

  2. 做到代码被重复使用, 一份代码可以在多个位置使用.

  3. 让代码更好理解更简单.

  4. 直接调用现有方法开发, 不必重复造轮子.

比如我们要重复计算造一个简单计算器,计算器需要反复判断两个数的和,则有如下代码:

public class Test {
    public static void main(String[] args) {
        int a = 0;
        int b = 5;
        int c =a+b;
        int ret =sum(a,b);
        System.out.println(c);
        System.out.println(ret);
    }
    //定义方法
    public static int sum(int a,int b) {
        return a+b;
    }
}


那么我们用方法该如何写呢?

2.2方法定义

image-20221108142046187

一个方法从写好到用起来?

1.定义方法,决定这个方法,返回值是什么类型?方法的名称是什么?形参有几个?什么类型?什么顺序?

2.使用这个方法,调用这个方法。方法名() ->看一下有几个参数,都是什么类型,都是什么顺序?

3.方法有没有返回值?要不要接收,拿什么类型接收?接收了返回值我拿返回值做什么?

举例子:

1.写一个判断闰年的方法

public class Method{
    // 方法定义
    public static boolean isLeapYear(int year){
        if((0 == year % 4 && 0 != year % 100) || 0 == year % 400){
            return true;
        }else{
            return false;
        }
    }
}

2.两个整数相加的方法

public class Method{
    // 方法的定义
    public static int add(int x, int y) {
        return x + y;
    }
}

注意事项:

  1. 修饰符:现阶段直接使用public static 固定搭配

  2. 返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成 void

  3. 方法名字:采用小驼峰命名

  4. 参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开

  5. 方法体:方法内部要执行的语句

  6. 在java当中,方法必须写在类当中

  7. 在java当中,方法不能嵌套定义

  8. 在java当中,没有方法声明一说

2.3方法调用的过程

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRNdSNH0-1668515523040)(./image-20221108144449702.png)]

【注意事项:】

  • 方法要写在类里面
  • 定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
  • 一个方法可以被多次调用.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uET5EwCc-1668515523040)(./image-20221108145925310.png)]
总结:

通过输出结果我们不难发现在主函数中,我们使用sum()调用方法的语法后,方法才会被调用,同时也看到方法可以被调用多次。每次调用根据传参不同,返回的值也不同。

那么有的同学问了,方法在底层是怎么调用的呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWclmF5Q-1668515523041)(./image-20221108150932445.png)]

总结一下:

1.方法的调用在栈上

2.当方法遇到return或者遇到花括号就结束了,开辟出来的空间会还给操作系统。

2.4实参与形参的关系

方法的形参相当于数学函数中的自变量,比如:1 + 2 + 3 + … + n的公式为
s u m ( n ) = ( 1 + n ) ∗ n / 2 sum(n) = (1+n)*n/2 sum(n)=(1+n)n/2
Java中方法的形参就相当于sum函数中的自变量n,用来接收sum函数在调用时传递的值的。形参的名字可以随意 取,对方法都没有任何影响,形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5UQ6jNbE-1668515523041)(./image-20221108153429637.png)]
注:

  • 形参名字可以随意取
  • 在Java中,实参的值永远都是拷贝到形参中,形参和实参本质是实体

实例:交换两个整形变量

public class TestMethod {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        swap(a, b);
        System.out.println("main: a = " + a + " b = " + b);
    }
    public static void swap(int x, int y) {
        int tmp = x;
        x = y;
        y = tmp;
        System.out.println("swap: x = " + x + " y = " + y);
    }
}
// 运行结果
swap: x = 20 y = 10
main: a = 10 b = 20

可以看到,在swap函数交换之后,形参x和y的值发生了改变,但是main方法中a和b还是交换之前的值,即没有交换成功。

这是为什么呢?

实参a和b是main方法中的两个变量,其空间在main方法的栈(一块特殊的内存空间)中,而形参x和y是swap方法中 的两个变量,x和y的空间在swap方法运行时的栈中,因此:实参a和b 与 形参x和y是两个没有任何关联性的变量, 在swap方法调用时,只是将实参a和b中的值拷贝了一份传递给了形参x和y,因此对形参x和y操作不会对实参a和b 产生任何影响。

注意:对于基础类型来说,形参相当于实参的一份临时拷贝,即传值调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-18NaUrA0-1668515523042)(./image-20221108154430787.png)]

实参x = 10 ,y = 20的值传给方法swap后,形参x,y会生成一块内存通过tmp交换了值,但是他们只是交换了自己这块空间的值,并没有改变实参方法结束后他们就被销毁了,所以对形参的操作不会影响实参。

2.5没有返回值的方法

方法的返回值是可选的. 有些时候可以没有的,没有时返回值类型必须写成void

class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        print(a, b);
    }
    public static void print(int x, int y) {
        System.out.println("x = " + x + " y = " + y);
    }
}


3.方法重载

3.1为什么需要方法重载

我们在实际应用过程中,求和时如果需要求整数和时我们可以定义方法名为sumInt,求小数和时sumFloat, **如果求的个数不同?参数不同呢?**这个时候java里面就引入了方法重载的概念。

public class TestMethod {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int ret = add(a, b);
        System.out.println("ret = " + ret);
        double a2 = 10.5;
        double b2 = 20.5;
        double ret2 = add(a2, b2);
        System.out.println("ret2 = " + ret2);
    }
    public static int add(int x, int y) {
        return x + y;
    }
}
Test.java:13: 错误: 不兼容的类型:double转换到int可能会有损失
        double ret2 = add(a2, b2);

由于参数类型不匹配, 所以不能直接使用现有的 add 方法.

还有一种解决问题的方法就是上面修改方法名,但是起名本来就是很麻烦的事情。

3.2方法重载的概念

在自然语言中,经常会出现“一词多义”的现象,比如:“好人”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S7Sfth4g-1668515523042)(./v2-98bffd16d5be93dc62cdc937166e822f_r.jpg)]

在自然语言中,一个词语如果有多重含义,那么就说该词语被重载了,具体代表什么含义需要结合具体的场景。

在Java中方法也是可以重载的。

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

public class Test1 {
    public static void main(String[] args) {
        add(1,2,3);
        add(1.7,15,2.8f);
        add(1,2)
    }
    public static int add(int a,int b) {
        return a+b+c;
    }
    public static int add(int a,int b,int c) {
        return a+b+c;
    }
    public static double add(double a,int b,float c) {
        return a+b+c;
    }
}

注意:

  • 方法名必须相同
  • 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不同
  • 与返回值类型是否相同无关

4.递归

从前有坐山,山上有座庙,庙里有个老和尚给小和尚将故事,讲的就是:

"从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:

“从前有座山,山上有座庙…” “从前有座山……” "

这样一直自己调用自己的我们就叫做递归

在这里插入图片描述

上面的故事有个共同的特征:自身中又包含了自己,该种思想在数学和编程中非常有用,因为有些时候,我们 遇到的问题直接并不好解决,但是发现将原问题拆分成其子问题之后,子问题与原问题有相同的解法,等子问题解 决之后,原问题就迎刃而解了。

4.1递归的概念

一个方法在执行过程中调用自身, 就称为 “递归”.

递归相当于数学上的 “数学归纳法”, 有一个起始条件, 然后有一个递推公式.

例如, 我们求 N!
起始条件: N = 1 的时候, N!1. 这个起始条件相当于递归的结束条件.
递归公式: 求 N! , 直接不好求, 可以把问题转换成 N! => N * (N-1)!

递归的必要条件:

  1. 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同

  2. 递归出口

**代码示例:**递归求N的阶乘

public class Test1 {
    public static void main(String[] args) {
        int n = 5;
        int ret = factor(n);
        System.out.println("ret = " + ret);
    }

    public static int factor(int n) {
        if (n == 1) {
            return 1;
        }
        return n * factor(n - 1); // factor 调用函数自身
    }
}

4.2递归执行过程分析

递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 “方法执行结束 之后, 回到调用位置继续往下执行”.

执行过程图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7H1IFcrq-1668515523043)(./image-20221108214209883.png)]

递归从名字上来讲就是递和归的过程,这里的递将5转化为:

5! = 5 4! 4 ! = 4 * 3! 3!= 3 * 2! 2 ! = 21!

一旦变为1时1的阶乘就是1我们返回去

4.3递归习题

代码示例1:

思考?递归思维的是将一个问题拆分成为小问题,小问题在拆分成更小的问题

这里我们先要拆一个4接着拆3最后拆2最后只剩下1,归回来的过程就产生了1 2 3 4

1234 / 10 = 123 …4

123 / 10 = 12…3

12 / 10 = 1…2

1 % 10 =1

这里的逻辑就是当a == 1时,我们结束递的过程,开始归于是我们有了如下代码

public class Test1 {
    public static void main(String[] args) {
        func(1234);
        
    }
    public static void func(int a) {
        if(a > 9) {
            func(a / 10);
        }
        System.out.println(a % 10);
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pd3Qocjm-1668515523043)(./image-20221108222807989.png)]


代码示例:求斐波那契数列?

 public static int fib(int n) {
        if (n == 1 || n == 2) {
            return 1;
        }
        return fib(n - 1) + fib(n - 2);
    }

但是斐波那契数列不适合用递归,浪费的空间和时间比较多,有些问题天生就适合递归,有些问题就适合循环。

像斐波那契数列就适合循环一点。

数组的定义与使用

5.数组的基本概念

5.1为什么要使用数组?

假如我们现在要存一个寝室的C#考试成绩数据,并对其进行输出,按照我们之前所掌握的知识,就会写出如下代码

public class Test {
    public static void main(String[] args) {
        double a = 88.8;
        double b = 95;
        double c = 87.4;
        double e = 57;
        double f = 60;
        double g = 70.5;
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(e);
        System.out.println(f);
        System.out.println(g);
    }
}
输出结果:
88.8
95.0
87.4
57.0
60.0
70.5

上面代码没有任何问题,但不好的是:如果有20名同学成绩呢,需要创建20个变量吗?有100个学生的成绩那不得 要创建100个变量。仔细观察这些学生成绩发现:所有成绩的类型都是相同的,那Java中存在可以存储相同类型多 个数据的类型吗?这里面就可以应用到数组

5.2什么是数组?

数组:可以看成是**相同类型元素的一个集合。**在内存中是一段连续的空间。

  1. 数组中存放的元素其类型相同
  2. 数组的空间是连在一起的
  3. 每个空间有自己的编号,起始位置的编号为0,即数组的下标。

5.3数组的创建及初始化

数组我们可以和普通数据类型类比来学

比如我们定义int a = 10,int是一个数据类型表示a变量是一个整形

数组我们可以这样来理解 int [] arr = {1,2,3,4,5} 这里int [] 表示 arr变量是一个整形数组,后面存放的数子是1,2,3,4,5

T[] 数组名 = new T[N]

T:表示数组中存放元素的类型

T[]:表示数组的类型

N:表示数组的长度

数组创建的三种方式:

public class Test {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6}; //这是我们经常使用的方式
        int[] arr1 = new int[]{1,2,3,4,5,6};
        int[] arr2 = new int[10];
    }
}
5.4数组的初始化

数组的初始化可以分为动态初始化和静态初始化

  1. **动态初始化:**在创建数组时,直接指定数组中元素的个数
 int[] arr2 = new int[10];
  1. 静态初始化在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
int[] arr1 = new int[]{1,2,3,4,5,6};
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};

【注意事项:】

  • 静态初始化虽然没有指定数组的长度,编译器在编译时会根据{}中元素个数来确定数组的长度。
  • 静态初始化时, {}中数据类型必须与[]前数据类型一致。
  • 静态初始化可以简写,省去后面的new T[]。
// 注意:虽然省去了new T[], 但是编译器编译代码时还是会还原
int[] array1 = {0,1,2,3,4,5,6,7,8,9};
double[] array2 = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = {"hell", "Java", "!!!"};

  • 数组也可以根据C语言的方式创建数组,但是不推荐
/*
该种定义方式不太友好,容易造成数组的类型就是int的误解
[]如果在类型之后,就表示数组类型,因此int[]结合在一块写意思更清晰
*/
int arr[] = {1, 2, 3};

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

  • -如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值,比如:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H9JV9cP5-1668515523044)(./image-20221109104900115.png)]

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

5.5数组的使用

数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过 下标访问其任意位置的元素。

int[]array = new int[]{10, 20, 30, 40, 50};
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]);
// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0])
运行结果
10
20
30
40
50
可以看到我们修改成功了,将下标为0的元素改为了从10改为了100
100

【注意事项】

  1. 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素

  2. 下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。

public class Test {
    public static void main(String[] args) {
        int[]array = new int[]{10, 20, 30, 40, 50};
        array[5] = 100;
        System.out.println(array[5]);//这里下标最大是4,我们访问了5
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O5BnwSb7-1668515523044)(./image-20221109110321677.png)]

在C语言中可能会输出随机值,而java会直接报错,所以我们可以得出java对安全性的要求还是很高的。


5.6如何遍历数组?

所谓 “遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印。

int[]array = new int[]{10, 20, 30, 40, 50};
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]);

上述代码可以起到对数组中元素遍历的目的,但问题是:

  1. 如果数组中增加了一个元素,就需要增加一条打印语句
  2. 如果输入中有100个元素,就需要写100个打印语句
  3. 如果现在要把打印修改为给数组中每个元素加1,修改起来非常麻烦。
//在java中我们可以通过数组名+.length来获取数组长度 
public static void main(String[] args) {
        int[]array = new int[]{10, 20, 30, 40, 50};
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
    }

还有另外一种遍历数组for-each

int[]array = new int[]{10, 20, 30, 40, 50};
        for(int x: array) {
            System.out.print(x+" ");
        }

for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.

还可以调用库方法(Arrays.toString(array))来遍历数组

public class Test {
    public static void main(String[] args) {
        int[]array = new int[]{10, 20, 30, 40, 50};
        System.out.println(Arrays.toString(array));
    }
}

这里是将数组转化为字符串进行输出,具体怎么操作是由java自身写好的方法,我们调用就行

5.7数组是引用数据类型

5.8 初始JVM的内存分布

内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:

  1. 程序运行时代码需要加载到内存
  2. 程序运行产生的中间数据要存放在内存
  3. 程序中的常量也要保存
  4. 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁

如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ktdJqXq-1668515523045)(./image-20221109111804846.png)]

比如图一我们很乱没有进行管理分配,我们要管理就很麻烦,图二就一目了然想找什么就放在某个地方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FX8EMIk4-1668515523045)(./image-20221109112036998.png)]

5.9基本类型变量与引用类型变量的区别

基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;

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

  public static void func() {
        int a = 10;
        int b = 20;
        int[] arr = new int[]{1,2,3};
    }

在上述代码中,a、b、arr,都是函数内部的变量,因此其空间都在main方法对应的栈帧中分配。 a、b是内置类型的变量,因此其空间中保存的就是给该变量初始化的值。

array是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tv6C2ixG-1668515523045)(./image-20221109112941940.png)]

从上图可以看到,从上图可以看到,**引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该 地址,引用变量便可以去操作对象。**有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。

数组是引用数据类型,引用变量其实就是一个变量来存地址的 ,局部变量是在栈上开辟的,对象在堆上。

局部变量就是方法内部定义的变量,不能和引用变量混到一起去

byte short int long float double char boolean 都是基本数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JaH6ni9i-1668515523046)(./image-20221109115802837.png)]

这两个变量都是局部变量,java中局部变量不初始化是没办法使用的。

5.9.1认识null

null 在 Java 中表示 “空引用” , 也就是一个不指向对象的引用.

public static void main(String[] args) {
       int [] arr = null;
        System.out.println(arr);
       System.out.println(arr[0]);
    }
//执行结果
null
Exception in thread "main" java.lang.NullPointerException
	at Test.main(Test.java:7)

null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操 作. 一旦尝试读写, 就会抛出 NullPointerException.

注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联.

这里的数组arr赋值为null表示不指向任何位置,所以可以直接输出null,但是我们如果通过下标去访问,因为他不指向任何位置,所以报错空指针异常!

数组现在学的差不多了,提几道问题和大家一起共同理解一下

  • 如果将一个数组的值赋给另外一个数组是什么情况?
public class Test {
    public static void main(String[] args) {
        int [] arr = {1,2,3,4,5,6};
        int [] arr1 = {5,6,7,8,9};
        arr = arr1;

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EEd2QM2x-1668515523046)(./image-20221109163852148.png)]

通过画图,想必大家应该能理解,刚开始的时候数组arr和数组arr1都在堆上生成了自己的对象,通过地址去访问他们,**当执行arr = arr1操作后实际上是让数组arr ——>指向了arr1所指向的对象,从而让arr1的值赋值了给了arr。**原来的内存空间没人指向会被操作系统回收。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlBdfTMO-1668515523046)(./image-20221114162637322.png)]

此时如果通过其中任何一个引用去访问一个值,另一个引用去访问的时候,也是会被改变的。

//什么意思呢就是如下代码
public class Test {
    public static void main(String[] args) {
        int [] arr = {1,2,3,4,5,6};
        int [] arr1 = {5,6,7,8,9};
        arr = arr1;
        arr[0] = 10;
        arr[1] = 12;
        for(int i:arr1) {
            System.out.print(i+" ");
        }
    }
}
运行结果:
    10 12 7 8 9

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTmsgA0r-1668515523047)(./image-20221114164014444.png)]

【注意:】 如果之后的继续对下标为0的元素进行赋值,会覆盖之前的值!

6.形参和实参问题

6.1【例子:】
public class Test2 {
    private static void func(int [] arr) {
        arr = new int [] {15,16,17};
    }
    public static void main(String[] args) {
        int [] arr1 = {1,2,3,4};
        func(arr1);
        System.out.println(Arrays.toString(arr1));
    }
}
运行结果:
    [1, 2, 3, 4]

大家认为此时数组arr1中存放的1,2,3,4呢?还是15,16,17呢? 答案是这样的!

为什么会产生这种情况?这其实和C语言中传值调用和传址调用有点类似但是又不完全相同,可以类比理解一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VP7RQE1b-1668515523047)(./image-20221114165740333.png)]

我们此时只是改变的形参arr的指向,并不会影响我们的实参arr1数组,所以输出的是数组arr1的元素没有发生改变。

那么下面这种情况呢?

public class Test2 {
    private static void func(int [] arr) {
        arr[0] = 999;
    }
    public static void main(String[] args) {
        int [] arr1 = {1,2,3,4};
        func(arr1);
        System.out.println(Arrays.toString(arr1));
    }
}
运行结果:
    [999, 2, 3, 4]

我们可以通过运行结果发现数组首元素的值被改为了999,那么是又是为什么呢?和上面有什么不一样呢?画图!!!

在这里插入图片描述

通过画图我们观察到:

  • 此时arr和arr1指向的是同一个对象,所以修改arr的值,arr1也会发送变化
  • 此时,这里传递的是引用,我们通过引用修改了原来的值

7.数组拷贝

7.1第一种方式

调用Arrays.copyOf来实现数组的拷贝,同时也可以进行扩容

import java.util.Arrays;

public class Test3 {
    public static void main(String[] args) {
        int [] arr = {1,2,3,4,5};
        //可以当作扩容来使用
        int [] ret = Arrays.copyOf(arr,arr.length*2);
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(ret));
    }
}
运行结果如下:
 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5, 0, 0, 0, 0, 0]

此时我们使用的Arrays类中的方法来进行拷贝,我们只需要会使用就行

7.2第二种方式

调用arraycopy来实现数组的拷贝,

public class Test3 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4];
        int[] copy = new int[array.length];
        //System.arraycopy(array,0,copy,0,array.length);
        System.arraycopy(array,1,copy,1,array.length-1);
        //这里表示从数组array下标为1的位置拷贝到数组copy数组下标为1的位置,后面的拷贝的长度
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(copy));
    }
        运行结果:
            [1, 2, 3, 4]
            [0, 2, 3, 4]
7.3第三种方式

调用Arrays.copyOfRange来实现数组拷贝

import java.util.Arrays;

public class Test4 {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        //可以当做扩容来用
        int[] ret = Arrays.copyOfRange(array,1,3);//[1,3)

        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(ret));

    }
}
运行结果:
    [1, 2, 3, 4, 5]
    [2, 3]

注意:这里的拷贝前开后闭,什么意思呢?就是包含下标为1的元素,不包括下标为3的元素

7.4克隆一个副本

关键字数组名 +. +clone()

public class Test {
    public static void main(String[] args) {
        int [] arr = {1,2,3,4};
        int [] arr1 = arr.clone();
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));
        System.out.println("=====================");
        arr1[0] = 99;
        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arr1));
    }
}
运行结果:
 [1, 2, 3, 4]
 [1, 2, 3, 4]
=====================
[1, 2, 3, 4]
[99, 2, 3, 4]

实际上克隆就是产生了一个副本,相当于new了一个新的对象,所以改变克隆后的的数组,并不会影响之前的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDuJipAA-1668515523048)(./image-20221115195614285.png)]

7.5深拷贝和浅拷贝问题

深拷贝:

如果一个数组中经过拷贝后,不能通过拷贝后的数组中的下标去修改原数组对应下标的值,那么此时就叫做深拷贝。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xdbaN4lK-1668515523048)(./image-20221115200615287.png)]

浅拷贝:

反之如果一个数组中经过拷贝后,能通过拷贝后的数组中的下标去修改原数组对应下标的值,那么此时就叫做浅拷贝。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HBPsGhvD-1668515523049)(./image-20221115201227037.png)]

注意:

数组当中存储的是基本类型数据时,不论怎么拷贝基本都不会出现什么问题,但如果存储的是引用数据类 型,拷贝时需要考虑深浅拷贝的问题

7.6查找数组中指定的元素
   public static void main(String[] args) {
        int[] arr = {1,2,3,10,5,6};
        System.out.println(find(arr, 10));
    }
    public static int find(int[] arr, int data) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == data) {
                return i;
            }
        }
        return -1; // 表示没有找到
    }
// 执行结果
3

8.二分查找

针对有序数组, 可以使用更高效的二分查找

什么是有序数组?

1,2,3,4,5,6,7,8

这样就是有序数组

以升序数组为例, 二分查找的思路是先取中间位置的元素, 然后使用待查找元素与数组中间元素进行比较:

  • 如果相等,即找到了返回该元素在数组中的下标
  • 如果小于,以类似方式到数组左半侧查找
  • 如果大于,以类似方式到数组右半侧查找
public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6};
        System.out.println(binarySearch(arr, 6));
    }
public static int binarySearch(int[] arr, int toFind) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {nt mid = (left + right) / 2;
            if (toFind < arr[mid]) {
                 // 去左侧区间找
                right = mid - 1;
            } else if (toFind > arr[mid]) {
               // 去右侧区间找
                left = mid + 1;
            } else {
            // 相等, 说明找到了
                return mid;
            }
        }
    // 循环结束, 说明没找到
        return -1;
    }
// 执行结果
5

可以看到, 针对一个长度为 10000 个元素的数组查找, 二分查找只需要循环 14 次就能完成查找. 随着数组元素个数 越多, 二分的优势就越大.

9.二维数组

二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组.

基本语法

数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
int[][] arr = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12}
    };
for (int row = 0; row < arr.length; row++) {
        for (int col = 0; col < arr[row].length; col++) {
        System.out.printf("%d\t", arr[row][col]);
        }
        System.out.println("");
        }
// 执行结果
        1 2 3 4
        5 6 7 8
        9 10 11 12

画个图带大家理解一下二维数组每个元素又是一个一维数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdOyR4pf-1668515523049)(./image-20221115202750527.png)]

不规则的二维数组:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-im5VJv7a-1668515523049)(./image-20221115202835353.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WbcKiGSf-1668515523050)(./2f68606562bfab6b82c43ccf2090a324.gif)]

关于java中方法和数组的介绍就到这里了,码字不易,如果本文对你有帮助的话来个一键三连把!
在这里插入图片描述

  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值