一、函数
函数的定义
什么是函数?
函数就是定义在类中的具有特定功能的一段独立小程序,也可以称之为方法
函数的格式
/* 修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,........){ 执行语句; return 返回值; } 返回值类型:函数运行后的结果的数据类型。 参数类型:是形式参数的数据类型。 形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数。 实际参数:传递给形式参数的具体数值。 return:用于结束函数。 返回值:该值会返回给调用者。 */
函数的特点
- 定义函数可以将功能代码进行封装
- 便于对该功能进行复用
- 函数只有被调用才会被执行
- 函数的出现提高了代码的复用性
- 对于函数没有具体返回值的情况,返回值类型用关键 字 void表示,那么该函数中的return语句如果在最后一 行可以省略不写。
- 注意: 函数中只能调用函数,不可以在函数内部定义函数。 定义函数时,函数的结果应该返回给调用者,交由调用者处 理。
函数的应用
两个明确
- 明确要定义的功能最后的结果是什么?
- 明确在定义该功能的过程中,是否需要未知内容参与运
函数的小应用
class FunctionDemo1 { public static void main(String[] args) { System.out.println(getAdd(5,8)); } /* 函数小练习 需求:定义一个功能,实现两个整数的加法运算 思路:1、因为需求是定义一个功能,所以应使用函数来实现 2、通过 需求发现需要把结果返回给调用者,所以该函数有返回值, 因为是两个整数进行运算,所以返回值是int类型 3、通过需求发现是两个未知整数进行未知运算,所以该函数有参数列表, 而且参数为三个,类型应为 两个int类型和一个char类型 */ //第一种实现方法 将运算后的值返回给调用者 public static int getAdd(int a, int b){ return a+b; } //第二种实现方法 将运算后的值在该函数内部打印 public static void getAdd1(int a,int b){ System.out.println(a+b); } /* 问题: getAdd和getAdd1这两个函数有什么一样么? 最大的区别就是getAdd1这个函数已经违背了我们定义函数的初衷,为什么呢? 因为我们定义这个函数的初衷只是为了对两个数进行求和运算,而getAdd1这个函数在 完成求和运算的基础上又做了打印操作。如果我们仅仅是为了打印两个数的和, 那么getAdd1这个函数这样定义倒是不为过,但是当需要拿两个数的和进行其他运算时, getAdd1这个函数就偏离了我们要实现的功能,所以,再定义函数时,尽可能的巴函数的 结果返回给调用者,让调用者去决定该结果的去向。 */ }
函数的重载
重载的概念
在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不 同即可。
重载的特点
与返回值类型无关,只看参数列表
重载的好处
方便阅读优化了程序设计
什么时候使用重载?
当功能相同,但功能主体不同时,可以使用重载
重载的应用
class FunctionDemo { public static void main(String[] args) { System.out.println("Hello World!"); } /*函数重载小练习 需求:利用函数重载分别实现打印99乘法表和根据用户输入打印乘法表 思路:1、通过需求发现两个函数都是打印乘法表,只不过一个是固定打印, 一个是根据用户输入数据打印,所以为方便阅读, 可以定义函数名相同的函数来实现 2、通过99乘法表发现,需要用到循环嵌套,而且是尖朝上, 所以利用尖朝上,可以通过控制内循环条件, 让条件随着外循环的变化而变化这一规律来完成程序设计 */ //打印固定99乘法表 public static void print99(){ /* for(int i=1;i<=9;i++){ //用来控制*号右边的数字 for(int j=1;j<=i;j++){ //用来控制*号左边的数字 //输出乘法表口诀 System.out.print(j+"*"+i+"="+(j*i)+"\t"); } System.out.println(); //输出换行 } */ /*发现使用重载后出现了重复代码, 经过观察发现该函数内部可以通过调用另一个函数来实现自己原有的功能, 而且代码相对简单 代码如下: */ print99(9); } //通过重载的形式实现打印 public static void print99(int num){ for(int i=1;i<=num;i++){ //用来控制*号右边的数字 for(int j=1;j<=i;j++){ //用来控制*号左边的数字 //输出乘法表口诀 System.out.print(j+"*"+i+"="+(j*i)+"\t"); } System.out.println(); //输出换行 } } }
函数重载判断
//函数重载判断 /* 判断下面所列举的函数是否与 public static void print(int n,char c,double num){}重载 1、public static void print(int x,char ch,double do){} //没有重载,因为参数类型一样 2、public static void print(double d,char c,int n){} //重载了,因为参数类型不一样 3、public static int print(char c,int a){} //重载了,因为参数类型不一样 4、public static int print(int a,char c,double d){} //没有重载,因为重载和返回值类型没有关系 5、public static void print(int a,char c){} //重载了,因为参数类型不一样 */
二、数组
数组的定义
概念:同一种数据类型的集合,相当于一个容器
数组的好处
可以给放入的元素进行编号,编号是从0开始的
几种常见的数组创建方式:
1、元素类型 [] 数组名 = new 元素类型[数组的长度] 如: int [] array = new int[5];
2、元素类型 数组名[] = new 元素类型[数组长度] 如: int array[] = new int [6];
3、元素类型 [] 数组名 = new 元素类型[]{元素,元素,元素.....} 如: int [] array = new int [] {1,2,3,4,5,6,7};
4、元素类型 [] 数组名 = {}; 如: int [] array = {1,2,3,4,5,6,7}
数组的内存分配及特点
内存结构
Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,有对空 间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
栈内存
用于存储局部变量,当数据使用完,所占空间会自动释放。
堆内存
数组和对象,通过new建立的实例都存放在堆内存中。
每一个实体都有内存地址值
实体中的变量都有默认初始化值
int——>0
double——>0.0
boolean——>false
char——>'\u0000' 空字符ASCII表
实体不在被使用,会在不确定的时间内被垃圾回收器回收
方法区
本地方法区:调用的是Windows系统底层资源
寄存器:和CPU有关
数组操作常见问题
1、数组脚标越界异常(ArrayIndexOutOfBoundsException)
class ArrayException { public static void main(String[] args) { int [] array = new int[3]; System.out.println(array[3]); /*由于访问了不存在的角标, 所以会出现数组脚标越界异常(ArrayIndexOutOfBoundsException) */ } }
2、空指针异常(NullPointerException)
class ArrayException { public static void main(String[] args) { int [] array =null; System.out.println(array[0]); /*由于array=null,所以array并没有指向实体,而却在操作实体中的元素 所以会出现空指针异常(NullPointerException)*/ } }
数组常见操作
打印数组内存地址值
public static void main(String[] args) { int [] arr = new int[]{10,22,35,47,56,64,78,82,96}; System.out.println(arr); }
其中“[”代表的是这是一个数组
“I”代表的是这个数组的类型是int类型
“15db9742”代表的是这个数组在内存中的位置,是一个十六进制的表现形式
数组遍历
class ArrayException { //实现数组遍历 public static void main(String[] args) { int [] array = {1,4,6,4,8,9,11,56}; for(int i=0;i<array.length;i++){ System.out.println(array[i]); } } }
数组获取最值
class ArrayException { //获取数组中的最大值 public static void main(String[] args) { int [] array = {1,4,6,4,8,9,11,56}; /*第一种方式:定义第三方变量,初始化为array[0], 让这个变量与数组中的元素进行比较,并记录较大的值 */ int temp = array[0]; for(int i=1;i<array.length;i++){ //对数组进行遍历,并从下标1开始 //取出较大的值 if(array[i]>temp) temp = array[i]; } System.out.println("数组array中的最大值是:"+temp); //第二种方式:定义第三方变量,初始化值为0,作为数组的下标使用 int index = 0; for(int i=1;i<array.length;i++){ if(array[i]>array[index]) index = i; } System.out.println("数组array中的最大值是:"+array[index]); } }
数组排序
选择排序
选择排序原理图
代码实现
class ArraySort1 { //选择排序 public static void main(String[] args) { int [] arr = {2,5,3,4,7,6,1}; System.out.sort("排序前"); getPrint(arr); System.out.println("排序后"); getSort(arr); } /* 选择排序: 思路:因为选择排序是拿一个元素与数组中的其他元素进行比较, 所以应该是嵌套循环,外层循环控制一个数个内层循环的所有数字 一一进行比较而比较的第一个数是这个数+1,而外层循环的最后一个 数,肯定是数组中最大(或最小)的数,所以是不用再次参与比较 的,所以外层循环循环次数应-1 */ //选择排序 public static void getSort(int [] arr){ for(int i = 0; i<arr.length-1;i++){ for(int j=i+1;j<arr.length;j++){ if(arr[i]>arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } }
但是通过观察发现这样写的代码只要条件成立每次都要进行换位操作,而计算机底层是二进制运算,相对比较麻烦,所以能不能每比较一轮换一下位置呢??所以代码可以修改为:
class ArraySort1 { //选择排序 public static void main(String[] args) { int [] arr = {2,5,3,4,7,6,1}; System.out.println("排序前"); getPrint(arr); System.out.println("排序后"); getSort(arr); } public static void getSort(int [] arr){ //用于记录最值下标 int index = 0; for(int i = 0; i<arr.length-1;i++){ index = i; for(int j=i+1;j<arr.length;j++){ if(arr[index]>arr[j]){ //记录最值下标 index = j; } } //进行换位操作 int temp = arr[i]; arr[i] = arr[index]; arr[index] = arr[i]; } }
冒泡排序
冒泡排序示例图:
代码实现:
class ArraySort1 { //数组中常用的两种排序方式 public static void main(String[] args) { int [] arr = {2,5,3,4,7,6,1}; System.out.println("排序前"); getPrint(arr); System.out.println("排序后"); getSortM(arr); getPrint(arr); } /*冒泡排序: 思路:外循环:因为冒泡排序是相邻两个数进行的比较,比较一轮后找出最值, 所以外循环比较次数也应该-1, 内循环:因为冒泡排序每循环一轮都会找出一个最值,所以内循环比较 次数也应该在-1的基础上还应该减去外循环的初始化值 */ //冒泡排序 public static void getSortM(int [] arr){ for(int i = 0; i<arr.length-1;i++){ for(int j=0;j<arr.length-1-i;j++){ if(arr[j]>arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }
基本查找
class ArrayFind1 { /*数组查找, 需求:给定一个数查找概该数是否存在,并返回改数在数组中的位置 */ public static void main(String[] args) { int [] arr = new int[]{10,22,35,47,56,64,78,82,96}; System.out.println("原数组"); print(arr); //要使用折半查找,数组中的元素必须是有序的(如:从小到大) System.out.println("使用一般查找方法"); System.out.println("要查找的数字的在数组中的下标为"+find(arr,64)); System.out.println("使用折半查找方法"); System.out.println("要查找的数字的在数组中的下标为:"+find1(arr,64)); } //遍历数组,用于显示数组元素 public static void print(int [] arr){ System.out.print("["); for(int i=0;i<arr.length;i++){ if(i==arr.length-1){ System.out.print(arr[i]+"]"); } else System.out.print(arr[i]+","); } System.out.println(); } /* 一般查找方法 思路:可以通过遍历数组的方式让数组中的每个元素与要查找的 数字进行比较,如果相等,这返回该元素所在的下标即可 如果该数不存在返回-1 */ public static int find(int [] arr, int num){ for(int i=0;i<arr.length;i++){ if(num==arr[i]) return i; } return -1; } /* 折半查找:对于有序数组,可以使用折半查找提高效率 思路:1、因为是折半查找,所以应该定义三个变量(min(最小下标),mid(中间下标),max(最大下标)) 2、因为需要进行循环,而循环次数不固定,所以应使用while循环, 而循环条件应为(min<=max)(因为min是最小下标,如果min>max还没 找到,那就说明该数组中并没有要查找的元素) 3、当要查找的数大于mid所在的元素时,应从min的位置应该发生改变,应为mid+1 然后在找出中间值继续循环,如果要查找的数小于此时的mid所在的 元素时,则max的位置应该发生改变,应为mid-1,然后再取中间值, 直到(min<=max)不成立,不成立返回-1 */ public static int find1(int [] arr,int num){ int min = 0; int max = arr.length-1; int mid = (min+max)/2; while(min<=max){ if(num==arr[mid]) return mid; else if(num>arr[mid]) min = mid+1; else if(num<arr[mid]) max = mid-1; mid = (min+max)/2; } return -1; } }
数组中的数组(二维数组)
二维数组的定义
1、元素类型 [][] 数组名 = new 元素类型[数组长度][数组中的数组的长度];
如 int [] [] array = new int [3] [4];
定义了名称为array的二维数组
二维数组中有3个一维数组
每一个一维数组中有4个元素
一维数组的名称分别为arr[0], arr[1], arr[2]
给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;
2、元素类型 [][] 数组名 = new 元素类型[4][];
如: int [] [] array = new int [3] [];
二维数组中有3个一维数组
每个一维数组都是默认初始化值null
可以对这个三个一维数组的长度分别进行初始化
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[2]