前言:大家好,我是笨鸟先飞,目前是一名在校大学生。本篇介绍java语法的基础部分
本篇文章偏基础适合在学java的小白🤭🤭。
如果文章有什么错误,请各位佬指教😭😭。
在此先感谢大家百忙之间抽出时间来看我写的博客。
个人主页:笨鸟先飞
系列专栏:java从0开始
网站推荐:面试前刷题,学习时刷题,新手都可以使用这个牛客网在有一定基础后可以使用力扣
文章目录
1.举个例子
我们在生活中需要问题时,可能需要向老师或者辅导员请教,在你问这个问题老师给你讲解了过后,过了一会儿张三又去问这个问题,于是老师给张三又讲了一遍了张三懂了,但是李四不懂啊!于是李四又去问老师了,后来老师不单独讲了,他在黑板又写了一遍,有那个同学不懂了就再去看一下。
这个在黑板写的一遍我们可以理解为我们定义的方法,同学不懂的去看(java里我们叫调用)就可以了
这个方法其实类似于C语言中的函数,大家可以类比学习一下!
在编程中其实也一样,某段代码的功能可能会被频繁的使用,如果在每个位置都重新实现一遍,会:
1.使程序变得繁琐
2. 开发效率低下,做了大量重复性的工作
3. 不利于维护,需要改动时,所有用到该段代码的位置都需要修改
4. 不利于复用
因此,在编程中,我们也可以将频繁使用的代码封装成"黑板"(方法),需要时直接看(即方法名–方法的入口 地址)使用即可,避免了一遍一遍的累赘。
2.方法概念及使用
2.1什么是方法?
方法就是一个代码片段. 类似于 C 语言中的 “函数”。方法存在的意义(不要背, 重在体会):
-
是能够模块化的组织代码(当代码规模比较复杂的时候).
-
做到代码被重复使用, 一份代码可以在多个位置使用.
-
让代码更好理解更简单.
-
直接调用现有方法开发, 不必重复造轮子.
比如我们要重复计算造一个简单计算器,计算器需要反复判断两个数的和,则有如下代码:
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方法定义
一个方法从写好到用起来?
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;
}
}
注意事项:
-
修饰符:现阶段直接使用public static 固定搭配
-
返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成 void
-
方法名字:采用小驼峰命名
-
参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
-
方法体:方法内部要执行的语句
-
在java当中,方法必须写在类当中
-
在java当中,方法不能嵌套定义
-
在java当中,没有方法声明一说
2.3方法调用的过程
调用方法—>传递参数—>找到方法地址—>执行被调方法的方法体—>被调方法结束返回—>回到主调方法继续往下
【注意事项:】
- 方法要写在类里面
- 定义方法的时候, 不会执行方法的代码. 只有调用的时候才会执行.
- 一个方法可以被多次调用.
总结:
通过输出结果我们不难发现在主函数中,我们使用sum()调用方法的语法后,方法才会被调用,同时也看到方法可以被调用多次。每次调用根据传参不同,返回的值也不同。
那么有的同学问了,方法在底层是怎么调用的呢?
总结一下:
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函数在调用时传递的值的。形参的名字可以随意 取,对方法都没有任何影响,形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。
注:
- 形参名字可以随意取
- 在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 产生任何影响。
注意:对于基础类型来说,形参相当于实参的一份临时拷贝,即传值调用
实参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方法重载的概念
在自然语言中,经常会出现“一词多义”的现象,比如:“好人”。
在自然语言中,一个词语如果有多重含义,那么就说该词语被重载了,具体代表什么含义需要结合具体的场景。
在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)!
递归的必要条件:
-
将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同
-
递归出口
**代码示例:**递归求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递归执行过程分析
递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 “方法执行结束 之后, 回到调用位置继续往下执行”.
执行过程图:
递归从名字上来讲就是递和归的过程,这里的递将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);
}
}
代码示例:求斐波那契数列?
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什么是数组?
数组:可以看成是**相同类型元素的一个集合。**在内存中是一段连续的空间。
- 数组中存放的元素其类型相同
- 数组的空间是连在一起的
- 每个空间有自己的编号,起始位置的编号为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数组的初始化
数组的初始化可以分为动态初始化和静态初始化
- **动态初始化:**在创建数组时,直接指定数组中元素的个数
int[] arr2 = new int[10];
- 静态初始化在创建数组时不直接指定数据元素个数,而直接将具体的数据内容进行指定
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};
-
如果没有对数组进行初始化,数组中元素有其默认值
-
-如果数组中存储元素类型为基类类型,默认值为基类类型对应的默认值,比如:
-
如果数组中存储元素类型为引用类型,默认值为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
【注意事项】
-
数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
-
下标从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
}
}
在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]);
上述代码可以起到对数组中元素遍历的目的,但问题是:
- 如果数组中增加了一个元素,就需要增加一条打印语句
- 如果输入中有100个元素,就需要写100个打印语句
- 如果现在要把打印修改为给数组中每个元素加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的内存分布
内存是一段连续的存储空间,主要用来存储程序运行时数据的。比如:
- 程序运行时代码需要加载到内存
- 程序运行产生的中间数据要存放在内存
- 程序中的常量也要保存
- 有些数据可能需要长时间存储,而有些数据当方法运行结束后就要被销毁
如果对内存中存储的数据不加区分的随意存储,那对内存管理起来将会非常麻烦。比如:
比如图一我们很乱没有进行管理分配,我们要管理就很麻烦,图二就一目了然想找什么就放在某个地方
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是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
从上图可以看到,从上图可以看到,**引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该 地址,引用变量便可以去操作对象。**有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。
数组是引用数据类型,引用变量其实就是一个变量来存地址的 ,局部变量是在栈上开辟的,对象在堆上。
局部变量就是方法内部定义的变量,不能和引用变量混到一起去
byte short int long float double char boolean 都是基本数据类型
这两个变量都是局部变量,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;
}
}
通过画图,想必大家应该能理解,刚开始的时候数组arr和数组arr1都在堆上生成了自己的对象,通过地址去访问他们,**当执行arr = arr1操作后实际上是让数组arr ——>指向了arr1所指向的对象,从而让arr1的值赋值了给了arr。**原来的内存空间没人指向会被操作系统回收。
此时如果通过其中任何一个引用去访问一个值,另一个引用去访问的时候,也是会被改变的。
//什么意思呢就是如下代码
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
【注意:】 如果之后的继续对下标为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语言中传值调用和传址调用有点类似但是又不完全相同,可以类比理解一下
我们此时只是改变的形参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了一个新的对象,所以改变克隆后的的数组,并不会影响之前的值。
7.5深拷贝和浅拷贝问题
深拷贝:
如果一个数组中经过拷贝后,不能通过拷贝后的数组中的下标去修改原数组对应下标的值,那么此时就叫做深拷贝。
浅拷贝:
反之如果一个数组中经过拷贝后,能通过拷贝后的数组中的下标去修改原数组对应下标的值,那么此时就叫做浅拷贝。
注意:
数组当中存储的是基本类型数据时,不论怎么拷贝基本都不会出现什么问题,但如果存储的是引用数据类 型,拷贝时需要考虑深浅拷贝的问题
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)]
不规则的二维数组:
关于java中方法和数组的介绍就到这里了,码字不易,如果本文对你有帮助的话来个一键三连把!