函数(从基础到方法调用)和数组(从基础到经典冒泡排序)
1.函数(方法)
-
方法是一段能完成独立功能的代码块。只需要写一次方法,可以多次被调用,提高了代码的复用性。
-
格式
<public> <static> 返回值类型 [void] 方法名([数据类型 变量名,数据类型1 变量名1,。。。]){ 方法体 [return 结果值]}
-
只要有return *** 就必须在前面定义的时候加上返回值类型,如果直接return;那么程序直接终止,后面的代码就得不到执行,这种方法仅限于返回值类型为void时
-
方法名定义时采用小驼峰 xxxXxxXxx,名字要符合方法的功能
class Demo{ public static void main(String[] args){ int c = 10; int d = 19; //调用方法体,但是值必须得定义一个符合类型值的存起来,括号里面得传参进去 int result = compareNum(c,d); System.out.println(result + "大"); } public static int compareNum(int a,int b){//这就是方法,这里的a和b与主方法中的a和b并不冲突,因为是在两个内存区中,方法中的变量只子方法中生效. if(a > b){ return a; }else{ return b; } } }
-
重点:值的传递,内存机制
class Demo1{ public static void main(String[] args){ int a = 10; int b = 19; compareNum(a,b); System.out.println(a); //此时的值还是10 } public static void compareNum(int a,int b){ a = 99; } }
内存机制:此图是以4为例,最开始的时候,这个类,和函数都在数据共享区,然后主方法先入栈,然后主方法中先开辟int a int b两个空间,分别赋值。然后调用compareNum()方法,这个方法也定义了两个变量分别是int a int b 主函数中的和compareNum()中的并不冲突。两个在两个内存区中,在调用时最后一步是只将主方法中的a和b的值传给了compareNum()中的两个变量,但原来主方法中的a 和 b的值并没有变化,变化的只是compareNum()中的变量的值。
方法的重载
-
在同一个类中两个或者多个函数(方法)名字相同,参数列表不一样(第一:参数个数不一样,或者,第二:对应索引位类型不一样)的两个方法是重载关系,跟返回值没有一点关系。目的是为了节约方法的功能命名和提高代码的可读性
-
如果不是重载,定义两个名字相同的方法,会报错
class Demo{ public static void main(String[] args){ int a = 1; int b = 2; int f = 3.0d; int c = add(a,b); System.out.println("调用的是两个参数的方法" + c); int d = add(a,b,c); System.out.println("调用的是三个参数的方法" + d); double e = add(a,f); System.out.println("调用的是两个参数的方法" + e); } public static int add(int a,int b){ return a + b; } public static int add(int a,int b,int c){//参数的个数不同 return a + b + c; } public static double add(int a,double b){//对应索引位的参数的类型不同 return a + b; } public static int add(int a,int b){//会报错,这个不满足重载的条件,重载和return的值没关系 return a; } }
题目:求1!+2!+3!..+10! 采用两种方法(一种用阶乘的方法,一种普通的for循环)
- 思路:分成两部分来看,第一部分求阶乘,第二部分求和
- 不用阶乘:
class Demo{
public static void main(String[] args){
int a = 3;
int sum = 0;
for(int i = 1;i <= a;i ++){//求和
sum += add(i);//调用a次这个add()方法,并且将之累加
// add(1) = 1!,add(2) = 2!,add(3) = 3!...
}
System.out.println("结果为:" + sum);
}
public static int add(int num){//求阶乘
int result = 1;
for(int i = num;i >= 1;i --){
result *= num;
}
return result;
}
}
- 用阶乘
class Demo1{
public static void main(String[] args){
int a = 3;
int sum = 0;
for(int i = 1;i <= a;i ++){//求和
sum += add(i);//调用a次这个add()方法,并且将之累加
// add(1) = 1!,add(2) = 2!,add(3) = 3!...
}
System.out.println("结果为:" + sum);
}
public static int add(int num){//采用递归求阶乘,要找到循环的的出口
int result = 1;
if(num > 1){
result = num * add(num -1);//递归
}
return result;
//当num = 3时,result = 3 * add(2) 当num = 2时,result = 2 * add(1)
//当num = 1时,result = 1此时就为循环的出口,然后方向循环输出结果
//递归的关键就是你要返回一个可以递归的入口,然后就是给一个可以出循环的出口
}
}
2.数组(长度是固定的)
-
定义:数据类型[] 数组名 = new 数据类型[整数]; 这个整数指的是数组的长度
数组不能直接输出,直接输出的是对应的内存中栈中的物理地址。
-
例如 int[] arr = new int[8];
其存储原理为:jvm的栈中存储其变量名,这个开辟的空间放置的是十六进制的地址,这个地址对应的是堆中的数组的内容。在定义时,根据给的长度固定开辟这么大的空间,然后里面存入所选定类型的默认值,int的默认值为0
基本数据类型的变量都存储在jvm中的栈中,栈的特点是存储空间小,但是存取速度快,特点是先进后出
引用数据类型的变量也在栈中,但是存在栈中的是一个十六进制的地址,根据这个地址,我们可以找到存在堆中的引用数据的内容。堆的特点是空间大,存取的速度相对于栈来说比较慢。
-
整数类型(byte、short、int、long)的默认值都是0
小数类型(float、double)的默认值是0.0
字符类型(char)的默认值为" ",也就是空格
布尔类型(boolean)的默认值为false
-
获得数组的长度使用数组的属性length,int len = arr.length
通过数组的变量名(数组的整个的地址),通过数组的变量名加上数组的下标或者索引可以对数组的每一个值进行操作,数组的下标不要越界使用。
arr[0] = 8;arr[1] = 10;.....arr[8] = 25;//此时会报错,因为arr[8]越界了
-
数组遍历
class Demo{ public static void main(String[] args){ int[] arr = new int[3]; arr[0] = 1; arr[1] = 2; arr[3] = 3; printArr(arr); } public static void printArr(int[] arr){ for(int i = 0;i < arr.length;i ++){ System.out.print(arr[i] + " "); } } }
-
数组的另外三种定义方式
-
int[] arr = new int[5]
-
char[] arr = {'a','b','c'}
这个数组的长度是由定义时的值的数量来决定 -
int[] arr = new int[]{1,2,3,4}
长度也不需要定义,它的长度是由数组中元素的个数来决定的,只有这一种才可以直接当作匿名的常量数组来被当作参数传递给函数class Demo{ public static void main(String[] args){ int[] arr = new int[3]; arr[0] = 1; arr[1] = 2; arr[3] = 3; printArr(arr);//这个实参arr传递进去的是堆中映射出来的物理地址,也就是引用 printArr(new int[]{1,2,3});//这个传递进去的是匿名常量数组,这有这种可以这么传,其他声明数组的方式都不行 } public static void printArr(int[] arr){ for(int i = 0;i < arr.length;i ++){ System.out.print(arr[i] + " "); } } }
-
求极值
方法:定义一个maxNum,然后遍历数组的每一个值,把每一个值都和maxNum进行比较,如果比maxNum大,那么就将其赋值给maxNum。
class Demo { public static void main(String[] args) { int[] arr = {10, 55, 66, 88, 42, 3}; sortArr(arr); } public static void sortArr(int[] arr){ int maxNum = -1; if(arr != null && arr.length != 0){//顺序不能颠倒,不然会报错,因为当arr为null时,它没有length for(int i = 0;i < arr.length;i ++){ if(arr[i] > maxNum){ maxNum = arr[i]; } } System.out.println("数组的最大值为"+maxNum); } } }
-
-
冒泡排序
方法:始终相邻两个比。然后碰到大的则两个交换顺序。时间复杂度O(n的平方)
注意:是从第0轮开始,轮号为0,因为当设计每一轮中的比较次数的时候,第一次的轮号为0,即循环时,i要从0开始。但是总轮数为4
class Test1 {
public static void main(String[] args) {
int[] arr = { 23, 25, 12, 7, 51 };
sortPop(arr);
for(int i = 0;i < arr.length;i ++){
System.out.print(arr[i] + " ");//输出结果为7 12 23 25 51
}
}
public static void sortPop(int[] arr) {
int temp = 0;
if (arr != null && arr.length != 0) {// 顺序不能颠倒,不然会报错,因为当arr为null时,它没有length
for (int i = 0; i < arr.length - 1; i++) { //总共循环的轮数为数组的长度减一
for(int j = 0; j < arr.length - i - 1; j ++){//每一轮两辆相比交换的次数,这里的i的第一次必须为0,因为是从第0轮开始
if(arr[j] > arr[j + 1]){ //此时为升序冒泡排序,因此碰到比自己大的直接交换顺序
temp = arr[j]; //定义一个临时的变量来当做交换的渠道。
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
}
-
倒序数组
方法1:先新建一个新数组。然后倒序遍历原来的数组,然后正序把这个数组的值存入新数组。
方法2:折半交换,先找到中间索引,也就是确定交换的次数。然后把最后一个和第一个交换,倒数第二个和正数第二个交换。一直交换到中间。如果是length是奇数,那么最后一次中间是数字和自己交换,如果length是偶数,那么刚刚好。最后得到结果。
class Test1 {
public static void main(String[] args) {
int[] arr = {12,45,6,89,90,1};
//方法一:
// int[] arr1 = new int[arr.length];//新建一个数组
// for(int i = arr.length - 1;i >= 0;i --){//倒序遍历老数组
// arr1[arr.length - 1 - i] = arr[i];//正序存入新数组
// }
// for(int i = 0;i < arr1.length;i ++){
// System.out.println(arr1[i]);
// }
reset(arr);
for(int i = 0;i < arr.length;i ++){
System.out.println(arr[i]);
}
}
//方法二:
public static void reset(int[] arr){
if(arr != null && arr.length != 0){//程序的严谨,防止传入空。然后两个判断的顺序不能颠倒。
for(int i = 0;i < arr.length/2;i ++){ //找到中间位置的索引,然后确定循环次数,也就是交换次数
int temp = arr[arr.length - i - 1]; //新建一个中间变量,方便交换
arr[arr.length - i - 1] = arr[i];//把前面的给后面
arr[i] = temp;//把后面的给前面
}
}
}
}
-
大题,综合应用合并和倒序输出数组
class Test1 { public static void main(String[] args) {//主函数,入口 int[] arr1 = {1,2,3,4}; int[] arr2 = {5,6,7,8}; int[] arr3 = combine(arr1, arr2); reverseArr(arr3); printArr(arr3); } public static void printArr(int[] arr3){ //循环输出数组的方法 for(int i = 0;i < arr3.length;i ++){ System.out.print(arr3[i] + "\t"); } } public static void reverseArr(int[] arr3){//倒序输出 if(arr3 != null && arr3.length !=0){//程序的严谨性,左右顺序不能变 for(int i = 0;i < arr3.length/2;i ++){//调换次数为arr3.length/2 int temp = arr3[i];//设置中间调换变量 arr3[i] = arr3[arr3.length - i - 1];//将后面的值赋值给前面的值 arr3[arr3.length - i - 1] = temp;//因为前面的值已经改变,因此只能用temp来当做中间变量进行赋值给后面,而原来的arr3[i]已经改变,因此不能直接用 } } } public static int[] combine(int[] arr1, int[] arr2) {//合并 int[] arr3 = new int[arr1.length + arr2.length];//新建一个数组,定义它的长度为你传进来的数组长度的和 for (int i = 0; i < arr1.length; i++) {//添加第一个数组进去 arr3[i] = arr1[i];//遍历添加 } for (int i = 0; i < arr2.length; i++) {//添加第二个数组进去 arr3[i + arr1.length] = arr2[i];//开始的位置得从第一次添加之后的末尾 } return arr3; } }
2.1 二维数组
-
定义:数据类型[][] [ ] [ ] 数组名 = new 数据类型[整数] [整数]
-
可以将其想象成二维直角坐标系。第一个参数为第几行,第二个参数是第几列
class Test1{ public static void main(String[] args){ int[][] arrDouble = new int[3][3]; arrDouble[0][0] = 1; arrDouble[0][1] = 1; arrDouble[0][2] = 1; arrDouble[1][0] = 2; arrDouble[1][1] = 2; arrDouble[1][2] = 2; arrDouble[2][0] = 3; arrDouble[2][1] = 3; arrDouble[2][2] = 3; for(int i = 0;i < arrDouble.length;i ++){ //行数 for(int j = 0;j < arrDouble[i].length;j ++){ //列数 //arrDouble[i]为获得的行的内容,然后这里是将对应行的每一列遍历出来 System.out.print(arrDouble[i][j] + "\t"); } System.out.println(); } //第二种声明方式 //int[][] arrDouble = {{1,1,1},{2,2,2},{3,3,3}} //第一行 第二行 第三行 } /* 0 1 2 0 1 1 1 1 2 2 2 2 3 3 3 */ }