[Java] 方法和数组
目录
1. 方法
1.2 什么是方法
1.2 方法的定义
1.3 方法的调用
1.4 方法的重载
1.5 递归
2. 一维数组
2.1 什么是数组
2.2 数组的创建
2.3 数组的初始化
2.4 遍历数组
2.5 引用数据类型
2.6 关于null
2.7 数组转字符串
2.8 数组元素的查找
2.9 数组的排序
2.10 数组的拷贝
2.11 数组逆序
2.12 Arrays类部分方法
3. 二维数组
3.1 二维数组的定义和初始化
3.2 数组的遍历
3.3 不规则二维数组
1. 方法
1.2 什么是方法
Java中的方法就类似于C语言当中的函数,是实现一种特定的,使用频率高的功能,专门解决一种问题的办法。例如:求一个数的开平方,判断一个数是不是素数。
方法也分为两种,一种是Java语言规定的方法,还有一种是程序员根据自己的需要自定义的一些方法。
当在解决重复问题时,设置一个方法解决重复问题,可以多次调用方法去解决重复问题,不用每次使用就需要写代码,减少代码冗余度。
1.2 方法的定义
修饰符 返回值类型 方法名(参数类型){
方法体代码;
(return 返回值;)
}
上面是方法的基本形式,举个例子,写一个判断一个数是不是素数的方法:
public static boolean isPrimeNumber(int n){for (int i = 2; i < Math.sqrt(n); i++) {if(n % i == 0){return false;}}return true;}
}
public static 是修饰符,boolean 是返回值类型,返回值类型必须与return返回的类型一致,isPrimeNumber是方法名,int n是参数,下面便是方法体,如果没有返回值,则返回值类型写void,不用写return。
方法不能嵌套定义,方法的命名一般是小驼峰,方法必须在类里面。
1.3 方法的调用
定义好一个方法后,需要在main函数里面进行调用,否则不会调用。
public static void main(String[] args) {Scanner sc = new Scanner(System.in);int a = sc.nextInt();boolean b = isPrimeNumber(a);if (b == true) {System.out.println(a + "是素数");}}
在调用方法时候如果方法需要传参,便需要在调用的时候传入对应类型的参数,然后调用方法。 形参是实参的一份临时拷贝,改变形参的值不会改变实参的值,因为形参和实参不是同一块地址,每次调用方法都需要在栈中调用一块空间,方法调用完销毁。
1.4 方法的重载
方法在调用时候可能需要传的参数的类型,顺序,数量不同,但都是同一种功能的方法,这时候就可以把两种方法用相同的名字命名,例如:
public static int add(int a,int b){return a + b;}public static double add(double a,double b){return a + b;}
上面代码是计算两个数的和的方法,但是传的参数不一样,可以用相同的名字来命名方法,这样调用这个方法时候既可以计算两个整型的和,也能计算两个双精度浮点数的和。
注意:
方法名必须相同
方法参数的类型,顺序,数量其中至少有一种不同
与方法的返回值类型无关。
按理来说同一块作用域不能有两个相同的标识符,但是为什么类里面可以有相同名字的两种方法呢?
其实不然,我们编写的方法名并不是真正存储在内存中的方法名,而编译器会将方法+后面的参数编译成一种方法签名供机器识别。
我们可以通过JDK自带的反汇编工具查看方法在机器中存储的真正名字。
1.先将.java文件编译成.class文件
2. 找到.class文件所在的目录,用控制台打开
3. 输入javap -v 字节码文件名就可以显示反汇编码了。
上面第一个方法名是add;(II)I,括号里面表示方法的参数是两个整型,后面表示返回值是整型,第二个方法名add(DD)D,括号里面表示方法的参数是两个double类型的,后面表示返回值是double类型。机器识别的是这种方法签名,区分方法的重载。
下面方法签名的一些符号:
1.5 递归
递归就是递推和回归的意思,在解决一些问题时,使用递归将复杂的问题简单化,就可以轻松求解。递归也就是方法重复调用自己,知道遇到停止条件,才停止调用,返回调用的值。
因此递归有两个重要的部分:
1. 递推公式
2. 终止条件
例如求n!;
public static void main(String[] args) {//求n的阶乘Scanner sc = new Scanner(System.in);int n = sc.nextInt();System.out.println(fib(n));}public static int fib(int a){if(a == 1){return 1;}return a * fib(a-1);}
上面代码中 a == 1是终止条件,a * fib(a-1)是递推公式。
但是递归也有它的坏处,那就是容易导致栈溢出,因为每次调用方法都需要在栈里面申请空间,如果问题过于复杂,申请的空间过多,就会导致栈溢出,例如用递归解决斐波那契数列问题,给的数太大,就会导致栈溢出。
2. 一维数组
2.1 什么是数组
数组可以理解为相同元素的集合,数组里面可以存储许多相同类型的数据,数组在内存中是连续存储的。
2.2 数组的创建
数组创建的基本形式,下面我以创建整型数组为例:
int[] a = new int[10];
int[]是数据类型,代表引用类型,a是变量名,称为引用变量也被称为引用,new是创建一个新的对象,后面中括号里是数组的大小。
2.3 数组的初始化
数组的初始化分为两种动态初始化和静态初始化:
动态初始化:
在创建数组的时候直接指定数组的大小,不指定数组内容,系统默认赋值为0:
int[] a = new int[10];
静态初始化:
在创建数组的时候不指定数组元素的个数,而是指定数组里面的值:
int[] a = new int[]{1,2,3,4,5};
静态变量虽然没有指定数组的大小,但是编译器会根据后面数据的内容来判断数组的大小。
静态变量初始化时候可以简写成:
int[] a = {1,2,3,4,5};
在Java中也支持C语言的定义数组的方法:
int a[] = {1,2,3,4,5};
当然不建议平时这样定义数组和初始化。
在静态初始化和动态初始化时也可以分开来写:
//静态初始化
int[] a;
a = new int[]{1,2,3,4,5};
//动态初始化
int[] b;
b = new int[10];
当然不能写成如下格式:
//错误
int[] a;
a = {1,2,3,4,5};
数组没有初始化的话,系统默认的值是:
数组中存储的是引用类型时的默认值是:null。
2.4 遍历数组
遍历数组有三种方法:
方法一:
下面方法中使用循环的方法来遍历数组并打印,里面用到.length的属性,.length是求数组长度的属性。
int[] a = new int[]{1,2,3,4,5};
for(int i = 0;i < a.length;i++) {System.out.print(a[i] + " ");
}
方法二:
下面是for循环的的另一种版本,能够更简便的遍历数组,把数组里的每个值传给定义的 x变量,并打印。
int[] a = new int[]{1,2,3,4,5};
for (int x,a) {System.out.print(x + " ");
}
方法二的优点是遍历数组简单,缺点是获取不了数组元素的下标,不能改变数组里面的值。
方法三:
这里用到了Arrays里面的toString方法,将数组传进去返回数字里面的值的字符串形式:
import java.util.Arraysint[] a = new int[]{1,2,3,4,5};
String b = Arrays.toString(a);
System.out.println(b);
输出结果是:[1,2,3,4,5]
2.5 引用数据类型
数组属于引用数据类型,引用数据类型就是存储到是数据在堆区创建的地址,存储都引用变量里面。下面先介绍一下JVM(java虚拟机):
程序都要在JVM中运行,在JVM中的栈区申请一块栈帧,存放方法的局部变量等,在堆区创建对象,用引用变量存放对象的地址,来使用对象。
下面是JVM里面的代码运行时的数据区:
为什么会有两个栈区?
本地方法栈执行的是由C/C++编写的方法。
虚拟机栈是执行的Java的方法的。
我们今天只用到堆区和虚拟机栈。
这里定义一个整型数组:
int[] a = new int[]{1,2,3,4,5};
int[] 是引用类型,a是引用变量,又称引用,new是创建新对象,后面是创建的新对象,它们在内存中存储如下:
引用变量a先在栈区申请一块空间,a是引用变量存放的是对象的地址,new创建的对象存放在堆区,a存放的地址,指向堆区里面创建的数组对象。
1. 一个引用变量只能指向一个对象。
2. 引用变量初始化赋值为null时,引用变量不指向任何对象。
3. 假设两个引用变量a 和 b,a=b的含义是:a指向的对象变成b指向的对象
4. 引用类型的引用变量存放的是对象的地址。
在Java中数组可以作为返回值,也可以作为方法的参数:
例如:
public static int[] arr(int[] a) {int[] b = new int[a.length];for(int i = 0;i < a.length;i++) {b[i] = a[i];return b;
}
2.6 关于null
null 在Java中被称为空引用,也就是不指向任何一个对象:
int[] a = null;
该引用不能进行读写操作,或者.length操作等,如果使用就会报错:
null相当于C语言中的NULL指针,表示的是一个无效的内存。
2.7 数组转字符串
在Java中有一个名为Arrays的类,里面有很多对数组处理的方法,Arrsys类里面的toString(数组名)的方法,能帮助我们快速的将数组转换成字符串:
public static void main(String[] args) {//数组转字符串int[] a = new int[]{1, 2, 3, 4, 5};String b = Arrays.toString(a);System.out.println(b);}
当然我们也可以自己书写一个将数组转换为字符串的方法:
public static void main(String[] args) {int[] a = new int[]{1,3,5,7,9};String b = myToString(a);System.out.println(b);
}public static String myToString(int[] a) {String arr = "[";for (int i = 0; i < a.length; i++) {arr += a[i];if(i != a.length-1) {arr += ", ";}}arr += "]";return arr;
}
2.8 数组元素的查找
方法一:顺序查找
一次遍历数组中的每个元素,知道遇到要查找的元素,返回元素下标。
public static void main(String[] args) {//查找数组中的元素,返回下标int[] a = new int[]{1,2,3,4,5};int b = 3;System.out.println(find(a,b));}public static int find(int[] a,int b) {for (int i = 0; i < a.length; i++) {if(b == a[i]) {return i;}}return -1;}
方法二:二分查找
二分查找的前提是数组元素是有序的,一次检查一半的元素,直到查找到元素,返回下标。
public static int fingOne(int[] a,int b) {int left = 0;int right = a.length - 1;while(left <= right) {int mid = (left + right) / 2;if(b > a[mid]) {left = mid + 1;}else if(b < a[mid]) {right = mid - 1;}else {return mid;}}return -1;}
方法三:系统自带的二分查找方法
int[] a = new int[]{1,2,3,4,5};int c = Arrays.binarySearch(a,3);System.out.println(c);
用到Arrays类里面的binarySearch(数组名,查找元素)方法 ,返回查找的数组下标,如果数组中没有查找元素,则返回 -(最后一次二分查找左下标 + 1)。
该方法的重载还可以实现指定数组返回进行二分查找元素:
int[] a = new int[]{1,2,3,4,5};int d = Arrays.binarySearch(a,1,3,3);System.out.println(d);
Arrays.binarySearch(数组名,范围开始下标,范围结束下标,查找元素);
从数组下标范围 [1,3)里面查找3元素,返回下标。
2.9 数组的排序
方法一:冒泡排序
升序:对数组中的元素从开始依次相邻两个进行比较;大的数后移,直到比较到最后两个数结束,数组中最大的数就在最后一个位置。循环往复,最后完成数组的升序排列:
public static void main(String[] args) {//冒泡排序(升序)int[] a = new int[]{1,22,5,33,66,55,9};mysort(a);System.out.println(Arrays.toString(a));}public static void mysort(int[] a) {boolean b = true;//轮数for (int i = 0; i < a.length-1; i++) {for (int j = 0; j < a.length-1-i; j++) {if(a[j] > a[j+1]) {int tmp = a[j];a[j] = a[j+1];a[j+1] = tmp;b = false;}}if(b == true) {return;}}return;}
方法二:Java自带的方法
Arrays类里面的.sort(数组名)方法,默认将数组内容升序排列:
int[] a = new int[]{1,22,5,33,66,55,9};Arrays.sort(a);System.out.println(Arrays.toString(a));
2.10 数组的拷贝
方法一:
自己创建方法实现
public static void main(String[] args) {//数组的拷贝int[] a = new int[]{1,2,3,4,5};int[] b = myCopyArray(a);System.out.println(Arrays.toString(b));}public static int[] myCopyArray(int[] a) {int[] b = new int[a.length];for (int i = 0; i < a.length; i++) {b[i] = a[i];}return b;}
方法二:
Java中自带的方法
Arrays类中的copyOf(拷贝的数组名,新数组的长度);
int[] a = new int[]{1,2,3,4,5};int[] c = Arrays.copyOf(a,a.length);System.out.println(Arrays.toString(c));
Arrays类里面还有一种方法可以指定数组范围拷贝:copyOfRange(拷贝的数组名,拷贝开始的下标,拷贝结束的下标);
int[] a = new int[]{1,2,3,4,5};int[] d = Arrays.copyOfRange(a,2,4);System.out.println(Arrays.toString(d));
注意:拷贝方法的底层方法代码由C/C++代码书写,被关键字native修饰的方法是由C/C++代码书写的,运行效率高,查看不了源代码。
2.11 数组逆序
将数组中的元素逆序输出:
public static void main(String[] args) {//数组逆序int[] a = new int[]{1,2,3,4,5};move(a);System.out.println(Arrays.toString(a));}public static void move(int[] a) {int left = 0;int right = a.length - 1;while(left < right) {int tmp = a[left];a[left] = a[right];a[right] = tmp;left++;right--;}}
2.12 Arrays类部分方法
查找数组指定元素(二分查找)
Arrays.binarySearch(数组名,查找元素);返回查找元素下标,若不存在查找元素,返回最后一次二分查找的左下标 + 1 取负数。
//查找数组指定元素int[] a = new int[]{1,2,3,4,5};int b = Arrays.binarySearch(a,4);int c = Arrays.binarySearch(a,2,4,3);System.out.println(b);System.out.println(c);
数组的拷贝
Arrays.copyOf(拷贝的数组名,新数组的大小);返回拷贝的新数组。
int[] a = new int[]{1,2,3,4,5};int[] c = Arrays.copyOf(a,a.length);System.out.println(Arrays.toString(c));
Arrays.copyOfRange(拷贝的数组名,拷贝开始下标,拷贝结束下标);拷贝数组指定范围的数据到新数组。
int[] a = new int[]{1,2,3,4,5};int[] d = Arrays.copyOfRange(a,2,4);System.out.println(Arrays.toString(d));
判断两个数组是否相等
Arrays.equals(数组1,数组2);比较数组1和数组2里面的内容是否相等。相等返回true,不相等返回false。
//比较两个数组是否相等int[] arr1 = new int[]{1,2,3,4,5};int[] arr2 = new int[]{1,2,3,4,5};boolean d = Arrays.equals(arr1,arr2);System.out.println(d);
数组的赋值
Arrays.fill(数组名,值);将值赋值给数组的所有元素。
//数组赋值int[] arr3 = new int[]{1,2,3,4,5};Arrays.fill(arr3,6);System.out.println(Arrays.toString(arr3));
fill方法的重载:Arrays.fill(数组名,赋值开始坐标,赋值结束坐标,值);指定数组范围赋值。
//数组赋值int[] arr3 = new int[]{1,2,3,4,5};Arrays.fill(arr3,2,4,6);System.out.println(Arrays.toString(arr3));
数组的排序(升序)
Arrays.sort(数组名);对数组内的元素进行升序排列。
int[] a = new int[]{1,22,5,33,66,55,9};Arrays.sort(a);System.out.println(Arrays.toString(a));
数组转换成字符串
Arrays.toString(数组名);将数组转换成字符串[1,2,3,4,5]。
//数组转换成字符串int[] arr4 = new int[]{1,2,3,4,5};System.out.println(Arrays.toString(arr4));
二维数组转换成字符串
Arrays.deepToString(数组名) ;将二维数组转换成[ [1,2,3],[4,5,6] ]。
//二维数组转换成字符串int[][] a = new int[][]{{1,2,3},{4,5,6}};System.out.println(Arrays.deepToString(a));
3. 二维数组
3.1 二维数组的定义和初始化
下面是定义并初始化数组的写法:
int[][] a = new int[2][3];int[][] b = new int[][]{{1,2,3},{4,5,6}};int[][] c = {{1,2,3},{4,5,6}};int[][] d;d = new int[2][3];int[][] f;f = new int[][]{{1,2,3},{4,5,6}};
3.2 数组的遍历
二维数组是特殊的一维数组,假设二维数组int[][] arr = new int[][]{{1,2,3},{4,5,6,}},arr指向的对象是一维数组,里面存放两个一维数组对象的地址,而两个地址位置分别是两个一维数组。
关系如下图:
方法一:循环遍历
public static void main(String[] args) {int[][] a = new int[][]{{1,2,3},{4,5,6}};for (int i = 0; i < a.length; i++) {for (int j = 0; j < a[i].length; j++) {System.out.print(a[i][j] + " ");}System.out.println();}}
方法二:for-each遍历
public static void main(String[] args) {int[][] a = new int[][]{{1,2,3},{4,5,6}};for(int[] b : a) {for(int c : b) {System.out.print(c + " ");}System.out.println();}}
方法三:Java自带的方法
Arrays.deepToString(数组名);作用是将二维数组转换成字符串
System.out.println(Arrays.deepToString(a));
打印结果:
3.3 不规则二维数组
Java中定义数组时不可以省略行的值,可以省略列的值:
public static void main(String[] args) {int[][] a = new int[2][];a[0] = new int[3];a[1] = new int[4];for (int i = 0; i < a.length; i++) {for (int j = 0; j < a[i].length; j++) {System.out.print(a[i][j] + " ");}System.out.println();}}
上面数组a就是不规则数组,第一行有三列,第二行有四列。
打印出来是:
在堆中存储如下: