三、方法
1.方法的简介
方法(method),就是一段具有特定功能的代码。可以通过调用名字在任意的位置上进行重复使用,从而达到代码简化的目的。
特点:
- java程序中最小的执行单元
- 提高了代码的重用性和可维护性
2.方法的定义
[访问权限修饰符] [其他的修饰符] 返回值类型 方法名([参数列表]) {
// 方法体
[return]
}
注意:
-
中空号表示可有可无的部分:即需要的时候要写上,不需要的时候该位置可以不用写
-
方法名是标识符,使用小驼峰命名法。
-
方法之间是平级的,不允许出现方法嵌套方法。
代码部分:
public class _01MethodDemo {
public static void main(String[] args) {
}
//定义一个方法:返回两个int类型的变量中的最大值
public static int max(int a, int b) {
if(a>b)
return a;
else
return b;
}
//定义一个方法:打印两个变量a(int) b(long)的和
public static void sum(int a,long b){
System.out.println(a+b);
}
}
简单练习定义了两个方法。
3.方法的修饰词
3.1访问权限修饰词
方法并不是在任何位置上都可以被调用,与权限修饰词有关。权限大,使用的地方就多,权限小,使用的地方就少。
权限修饰词 | 本类中 | 同包下 | 子类中 | 其他位置 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
默认的 | √ | √ | ||
private | √ |
3.2 其他修饰词
static: 静态的方法, 调用的时候使用类名直接调用。 也可以在本类中的其他方法中直接写方法名表示调用
final: 最终,最后。 final修饰的方法在子类中不能被修改了。因为是最终的了。
4. 方法的返回值
1.方法在定义时,必须规定方法的返回值类型是什么,
2.方法的返回值类型位置可以书写两大类型
第一种类型,void,无类型,表示方法在执行完毕后,不需要返回任何数据。
第二种类型,java中的任一类型,包括程序员自己定义的类型。3.return关键字:
--返回,归还的含义。
--在方法中表示结束方法,因此return后面不能有其他代码,没有任何意义,执行不到。
--void类型的方法,return可加可不加。
-- 返回值类型是其他类型的方法,return关键字必须添加,并且return后面必须有一个该返回值类型的变量或者表达式。表示方法执行完毕后,最终数据要返回给调用者。
注意:谁是调用者,调用该方法的所在处就是调用者。
测试代码:
public class _02MethodDemo {
public static void main(String[] args) {
}
//定义一个void形式的方法m1,
public static void m1(){
int a =1;
int b =2;
int c = a+b;
return ;//return 关键字表示方法结束,可加可不加
}
//定义一个返回值为int类型的方法m2
public static int m2(){
int a =1;
int b =2;
int c = a+b;
return c+1;
}
public static String m3(){
int a =1;
int b =2;
int c = a+b;
return c+"";
}
}
5.方法的参数列表
方法的参数列表:
1.参数列表具体指的是定义期间方法名后的参数的类型列表
类型非常重要,列表指的是从左到右的顺序。
名字不重要。
2.参数列表的语法: 是0个以上的变量的声明语法。没有初始化操作。
3.参数列表里的参数名,称为形式参数,简称形参。
4.形式参数用于规定调用者在调用期间需要传入的数据的具体类型。5.方法的重载:同一个类中,方法名相同,参数类型列表不同(方法签名不同)。
扩展知识点:
方法签名:方法名+参数的类型列表
比如:m1(int a, long b,String c)的方法签名:m1(int,long,String)
在同一个类体中,不能存在方法签名相同的方法
m1(String x, long y,int z) 的方法签名: m1(String,long,int),这种可以跟上面m1同时出现,也就是方法的重载。
代码案例
public class _03MethodDemo {
public static void main(String[] args) {
}
//案例1:
public static void m1(){
System.out.println("--m1方法--");
}
//案例2: 方法签名 m2(int)
public static void m2(int a){
System.out.println("--m2方法--"+a);
}
public static void m3(int a){
System.out.println("--m3方法--"+a);
}
// 和上面的m3(int)是重载关系
public static void m3(int a,int b){
System.out.println("--m3方法--"+a);
}
// 定义方法:计算三个数中的最大值。
public static int max(int a,int b,int c){
int max = Integer.MIN_VALUE; //int的最小值
if(a > b && a > c){
max = a;
}else if(b > a && b > c){
max = b;
}else{
max = c;
}
return max;
}
// 定义方法:计算四个数中的最大值。
public static int max(int a,int b,int c,int d){
int max = Integer.MIN_VALUE; //int的最小值
return max;
}
}
6.方法的调用
一个方法,设计完成之后,是不会自动执行的。如果我们希望执行这个方法体中的代码,需要进行方法调用。
方法的调用: 没有学习面向对象前,方法都使用static修饰。
调用语法: 方法名(有参传参)。
注意:void修饰的方法,一般上述语法调用
其他返回值类型的方法,一般都是如下形式
返回值类型 变量 = 方法名(有参传参)
无参数方法的调用
语法: 方法名();
实际参数,简称实参。
1. 位于方法被调用的位置,即调用时传入的数据或者变量。
2. 实际上,就是给形式参数进行赋值真实的数据。
3. 赋值时,实际参数要与形式参数的个数,类型,顺序一一对应上。
将实际参数赋值给形式参数的这个过程,我们可以称之为传参。
有参数方法的调用
语法: 方法名(实际参数列表);
演示代码:
public class _04MethodDemo {
public static void main(String[] args) {
/* 先演示没有形参列表的方法调用*/
//void形式的方法调用
System.out.println("--开始前--");
myMethod1();
System.out.println("--后续代码--");
//返回值是java类型的方法调用
int s = myMethod2();
System.out.println("s="+s);
System.out.println(myMethod2()+1);
}
public static void myMethod1(){
int a = 10;
int b = 20;
int c = a + b;
System.out.println(c);
}
// 需求:计算1~100的和。
public static int myMethod2(){
int sum = 0;
for (int i = 1; i < 101; i++) {
sum+=i;
}
return sum;
}
}
总结 1.没有参数的方法调用,括号里面是空着的。 2.有参数的方法调用: -实际参数,简称实参。在调用时,传入到方法小括号里的变量,或者是具体值。 -实参的作用:就是用于给形参赋值(传值),这个过程,成为传参。 3.void形式的方法调用 -- 方法名(有形参传入实参) 4.有具体返回值的方法调用 -返回值类型 变量= 方法名(有形参传入实参)
7.递归调用
递归方法是一种直接或间接调用自身的方法。递归的基本思想是将大问题分解为小问题,直到问题变得足够简单以至于可以直接解决,然后将这些简单问题的解合并起来,从而解决整个大问题。
说大白话递归就是自己调用自己。
测试代码:
public class _02RecursionDemo {
public static void main(String[] args) {
// long sum = 0;
// long mul = 1;
// for (int i = 1; i <= 20; i++) {
// mul = mul * i;
// sum = sum + mul;
// }
// System.out.println(mul);
System.out.println(f(20));
int n = 20;
System.out.println("1~"+n+"的和: "+mySum(n));
}
public static long f(int n) {
//需要设置递归的终止操作。n为1时,不需要调用递归方法了。
if(n==1){
return 1;
}
return f(n-1)*n;
}
//递归计算1~n的和。
public static int mySum(int n) {
if(n==1){
return 1;
}
return mySum(n-1)+n;
}
}
需要注意的是,实际开发中,也可以减少使用递归,因为它占用的资源很大。
四、数组
1.数组的定义
数组是一种数据结构,用于存储相同类型数据的集合。数组中的每个元素都可以通过索引来访问,索引通常是从0开始的。Java数组是静态的,这意味着一旦创建,其大小就不能改变。但是,你可以使用ArrayList等集合类来模拟动态数组。
数组的实例化:
1. 使用new的两种方式: 动态初始化
第一种方式: new+直接初始化具体元素 如:int[] arr = new int[]{1,2,3,4,5}
第二种方式: new+长度 如: int arr[] = new int[5]
2. 静态初始化
数据类型[] 变量 = {元素1,元素2,...元素n}
测试代码如下:
import java.util.Arrays;
public class _01ArrayDemo {
public static void main(String[] args) {
//new+直接初始化具体元素
String[] names = new String[]{"小红","小明","小强","张三","李四"};
//new+长度: 注意,其实是有长度个默认元素
int[] nums = new int[5]; // byte,short,int,long对应的默认元素是0
System.out.println(Arrays.toString(nums));
double[] prices = new double[5]; // float double 对应的默认元素0.0
System.out.println(Arrays.toString(prices));
char[] chars = new char[5]; //char 对应的默认元素 \u0000
System.out.println(Arrays.toString(chars));
boolean[] bools = new boolean[5]; //boolean 对应的默认元素 false
System.out.println(Arrays.toString(bools));
String[] adds = new String[5]; //引用类型 对应的默认元素 null
System.out.println(Arrays.toString(adds));
//使用静态初始化的方式,实例化一个数组, 注意:该方式不能先声明,再初始化
int[] numbers = {1,2,3,4,5};
}
}
2. 数组的访问
在Java中,访问数组元素是通过使用索引来实现的。索引是一个整数值,用于指定数组中你想要访问的特定元素的位置。数组索引通常是从0开始的,这意味着第一个元素的索引是0,第二个元素的索引是1,依此类推。
数组的访问:
- 通过下标访问: 下标就是元素对应的索引值。 下标从0开始,最后一个元素下标length-1
- 数组的长度: 变量名.length
- 如何通过下标访问: 变量名[index]
测试代码:
public class _02ArrayDemo {
public static void main(String[] args) {
int[] ages = new int[]{20,18,19,20,19,30};
//获取ages中的第一个元素
int first = ages[0];
System.out.println("first: " + first);
//获取ages中的第4个元素
int four = ages[3];
System.out.println("four: " + four);
//打印ages的长度
System.out.println("长度:"+ages.length);
//直接打印ages中的最后一个元素
System.out.println("最后一个元素:"+ages[ages.length-1]);
//将年龄39,存入到第二个位置
ages[1] = 39;
//将年龄50,存入到最后一个位置
ages[ages.length-1] = 50;
System.out.println(Arrays.toString(ages));
//创建一个具有5个长度的long类型数组
long[] ns = new long[5];
//将数字10,20,30,40,50分别存入。
ns[0] = 10;
ns[1] = 20;
ns[2] = 30;
ns[3] = 40;
ns[4] = 50;
System.out.println(Arrays.toString(ns));
//将数字1~10存入到int类型的数组scores里
int[] scores = new int[10];
for (int i = 0; i < scores.length ; i++) {
scores[i]=i+1;
}
System.out.println(Arrays.toString(scores));
}
}
解析:Arrays.toString(数组名):Arrays.toString()
是java.util.Arrays
类的一个静态方法,它用于将数组转换为其字符串表示形式。这个方法对于打印数组内容到控制台或记录日志等场景非常有用,因为它可以很方便地将数组中的所有元素转换为一个易于阅读的字符串格式。
3. 数组越界(异常)
数组下标越界异常:ArrayIndexOutOfBoundsException
1. 什么时候发生的?
在运行期间发生的。
2. 怎么发生的?
当输入的下标大于等于数组的长度或者是小于0发生的。
3. 运行时异常,如果不做任何处理操作,那么jvm会暴力终止该程序。
空指针异常:NullPointerException
1. 运行时异常,运行期间发生的。
2. 如何发生的, 即使用null去访问各种方法或者属性。
比如: null.length() null.name等等操作。
上述两个异常,记一下子
测试代码:
数组越界:
package com.basic.day05.zuoye;
public class Test1 {
public static void main(String[] args) {
int[] a = new int[10];
for (int i = 0; i < a.length; i++) {
int m=a[0];
if (a[i]>a[i+1]){
m=a[i];
}
}
}
}
结果:
分析:发生了数组越界异常,上述代码当i为length-1时,i+1为length,超出了数组的最大值。所以出现了异常。
空指针异常:
代码:
package com.basic.day05.zuoye;
import java.lang.reflect.Array;
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) {
String[] str = new String[]{"王冕","","",""," ",null};
for (int i = 0; i < str.length; i++) {
System.out.println(str[i].length());
}
}
}
结果:
发生了空指针异常,null调用length()方法出现了异常。
4. 数组的遍历(for循环和foreach循环)
在Java中,数组的遍历(即逐个访问数组中的每个元素)可以通过多种方式实现。对于数组的遍历,传统for循环和增强型for循环(foreach循环)是最常用和最简单的方法。
数组的遍历
1. 使用经典for循环,通过下标进行遍历
2. 使用foreach循环:也叫增强for循环。底层使用的是迭代器。
for(元素类型 变量名:数组 | 集合 | 枚举){
}
3. 两者的对比
- 增强for循环中,没有下标的概念
- 增强for循环中,不能对数组中的元素进行改动
- 增强for循环比遍历下标的执行效率要高
代码为:
import java.util.Arrays;
public class _04ArrayDemo {
public static void main(String[] args) {
int[] a = new int[5];
a[0] = 10;
a[1] = 20;
a[2] = 30;
a[3] = 40;
a[4] = 50;
//直接打印数组的变量
System.out.println("数组变量:"+a);//直接打印数组名,得到的是类全名+@+内存地址的16进制数字
//调用Array.toString(a);
System.out.println(Arrays.toString(a));
//使用经典for循环,通过下标,依次遍历
for (int i = 0; i < a.length; i++) {
// System.out.println(a[i]);
System.out.print(a[i]+" ");
}
System.out.println();
System.out.println("--------增强for循环--------");
// 使用foreach循环遍历上面的数组nums
for(int n:a){
System.out.print(n+" ");
}
}
}
注意增强for循环的形式 ,括号里面是冒号。每次迭代时,数组a中的当前元素值会被赋值给变量n
,然后执行循环体中的代码输出n的值。
for(int n : a){
System.out.print(n+" ");
}
5. 排序
5.1 选择排序
数组的排序之: 选择排序
原理:
第一轮:拿0索引处的元素与后面的元素一一做比较,满足条件就交换。每次交换都保证0索引处是较小值。
一轮结束,0索引处,就是最小值
第二轮:拿1索引处的元素与后面的元素一一做比较,满足条件就交换。每次交换都保证1索引处是较小值。
一轮结束,1索引处,就是第二最小值
每轮依次比较下去。
比较的轮数: 元素个数-1
通过分析: 轮数是有外层循环控制,
每一轮的比较由内层循环控制
0-1,2,3,4,5
1-2,3,4,5
2-3,4,5
3-4,5
4-5
代码如下:
public class _05ArrayDemoPX {
public static void main(String[] args) {
int[] nums ={6,5,3,2,4,1};
System.out.println(Arrays.toString(nums));
//控制轮数
for (int i = 0; i < nums.length-1; i++) {
//控制比较次数
for (int j = i+1; j < nums.length; j++) {
// //获取要找的那个元素
// int el = nums[i];
//nums[i]: 就是每轮要判断是否是最小的元素位置
// nums[j]:就是一次要比较的后续元素
if(nums[i]>nums[j]){
//定义一个临时变量,保存nums[i]的元素
int temp = nums[i];
//将j处的元素存入i中
nums[i] = nums[j];
//将temp里的i中的元素给到j。
nums[j] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
}
}
输出结果:
5.2 插入排序
将一个数组的第一个元素,看成是有序的。然后拿第二个元素与前面的元素从左到右依次做比较。如果小于等于某一个元素
则进行插入。该位置处及其以后的有序元素都要向后移动一位。
原数组: 4 6 3 1 2 7
第一次: 4 | 6 3 1 2 7
比较一次
第二次: 4 6 | 3 1 2 7
3和4比较。 需要插入
3存起来,每个元素向后移动
结果:
3 4 6 | 1 2 7
第三次: 需要插入,1存起来, 移动元素
结果:
1 3 4 6 | 2 7
第四次: 需要插入 2存起来 移动元素
结果:1 2 3 4 6| 7
第五次: 不需要插入
代码为
public class _07ArrayDemoPX {
public static void main(String[] args) {
int[] a = {1,6,3,4,2,7};
System.out.println("排序前: "+Arrays.toString(a));
int j;
//从下标1开始,拿到对应元素。0位置上的元素是已经排好序的了
for (int i = 1; i < a.length; i++) {
//拿到的这个元素,判断一下,是否要插入。小于前面的数,就表示要插入
if (a[i] < a[i-1]) {
int temp = a[i];//既然要插入,那么该元素需要存起来,避免被覆盖
//这个循环的目的是要查找待插入的位置
//从后面一次向前查找待插入位置
for (j = i-1; j >= 0&&a[j]>temp; j--) {
//j位置上的元素比temp大,说明j位置的元素要向后移动,空出位置
a[j+1] = a[j];
}
//循环退出时,j位置的元素应该是小于或等于temp,那么j+1就是待插入的位置
a[j+1]=temp;
}
}
System.out.println("插入排序后:"+Arrays.toString(a));
}
}
结果:
如果不理解指路:8.2_1_插入排序_哔哩哔哩_bilibili(王道数据结构课程,插入排序)
5.3 冒泡排序
冒泡排序:
从左到右,紧挨着的两个元素进行比较,满足条件就交换。
数组里有6元素。
第一轮: 能找到最大值,冒泡到最右边。
比较五次
第二轮: 第二大元素冒泡到倒数第二个位置。
比较4次
.............
第五轮: 第五大元素冒泡的正数的第二个位置。 最后一个数不再需要比较了
注意: 每一轮比较时,都是从最左边的两个元素开始的。
6个元素:
i=0 j=5
i=1 j=4
i=2 j=3
i=3 j=2
i=4 j=1 j < length-1-i
代码为:
public class _08ArrayDemo {
public static void main(String[] args) {
int[] nums = {6,2,4,7,1,5};
//外层循环控制着轮数,轮数是leng-1轮
for (int i = 0; i < nums.length-1; i++) {
//内层是控制着每轮如何比较. 每次都要从左边开始比较,因此j从0开始
for (int j = 0; j < nums.length-1-i ; j++) {
// 如果前面的大于后面的,就做交换操作
if (nums[j] > nums[j+1]) {
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
}
}
结果:
6.查找
6.1 顺序查找法
顺序查找法(Sequential Search)是一种简单直观的查找方法,它按照顺序逐一地检查每个元素,直到找到所需的元素或搜索完整个序列为止。顺序查找法适用于未排序或排序的列表(或数组),但通常对于未排序的列表更为常。
顺序查找的思想:
- 从数据结构(如数组或链表)的一端开始,逐个检查每个元素。
- 如果找到所需的元素,则返回该元素的位置(或元素本身,取决于具体实现)。
- 如果搜索完整个数据结构都没有找到所需的元素,则返回一个表示未找到的值(如-1或null)。
优点:
- 实现简单,容易理解。
- 对数据是否排序没有要求。
缺点:
- 查找效率低。
代码:
public class _09ArrayDemo {
public static void main(String[] args) {
int[] nums = {100,23,48,56,10,78};
//需求:查找数组中是否有56,如果有,返回其坐标,如果没有返回-1
int index = searchElement(nums,23);
System.out.println("index:"+index);
}
/**
* 需求:查找int类型的数组arr中是否有元素element,如果有,返回其坐标,如果没有返回-1
*/
public static int searchElement(int[] arr, int element) {
int index = -1;
for (int i = 0; i < arr.length; i++) {
if(arr[i] == element){
index = i;
break;
}
}
return index;
}
}
6.2二分查找法
二分查找法(Binary Search),也称为折半查找法,是一种在有序数组中查找特定元素的高效算法。它的基本思想是通过不断缩小查找范围来快速定位目标值的位置。
具体步骤如下:
- 确定查找范围:初始化查找范围的左右边界,通常左边界为0,右边界为数组长度减1。
- 计算中间索引:在每一次迭代中,计算中间索引,即
mid = left + right。
- 比较目标值与中间元素:将目标值与中间元素进行比较。
- 如果目标值等于中间元素,则查找成功,返回中间元素的索引。
- 如果目标值小于中间元素,则目标值必定位于中间元素的左侧,因此将右边界更新为
mid - 1
。- 如果目标值大于中间元素,则目标值必定位于中间元素的右侧,因此将左边界更新为
mid + 1
。- 重复查找:重复步骤2和步骤3,直到找到目标值或查找范围为空(即
left > right
)为止。如果查找范围为空,则说明目标值不存在于数组中,返回相应的失败信息(如-1)。
代码:
public class _10ArrayDemo {
public static void main(String[] args) {
int[] nums = {1,4,6,7,9,10};
int index = binarySearch(nums,7);
System.out.println(index);
}
/**
* 写一个使用二分查找算法,从int类型的数组arr中找元素element的方法
* 找到,返回其坐标,没找到,返回-1
*/
public static int binarySearch(int[] arr, int element) {
int min = 0, max = arr.length - 1;
// min<=max时,表示还没有找到该元素, min=max时,是最后一次查找
while (min <= max) {
// 找到中间的元素下标
int mid = (min + max) / 2;
if (arr[mid] == element) {
//如果mid位置上的元素就是我们要找的,就返回下标
return mid;
}else if (arr[mid] < element) { // 表示要找的元素,在中间值的右侧
min = mid + 1; //将min设置为中间下标+1. 然后重新循环
}else { //表示要找的元素,在中间值的左侧
max = mid - 1; //将max设置为中间下标-1. 然后重新循环
}
}
//循环结束都没有遇到return,说明没有找到,就返回-1
return -1;
}
}
7.Arrays工具类
学习一下Arrays工具类的用法
- toString():将数组转换为字符串形式。 "["+元素1+","+元素2+","+.....+"]"
- binarySearch():在已排序的数组中查找指定元素的索引。
- fill():将数组的所有元素都设置为指定值。
- sort(数组):对数组进行排序。(默认从大到小的排序方式)
- copyOf():将一个数组的部分或全部元素复制到一个新数组中。可以复制大数组,也可以是小数组。大的添加默认值,小的剪切掉。
注意:上述的方法直接使用Arrays.调用
代码部分:
public class _11ArrayDemo {
public static void main(String[] args) {
int[] nums = {1,2,3,4,5};
String info = Arrays.toString(nums);
System.out.println(info);//[5, 4, 3, 2, 1]
System.out.println(Arrays.toString(nums)); //[5, 4, 3, 2, 1]
//注意:工具类里的二分查找法,必须要数组升序排序
int index = Arrays.binarySearch(nums,4);
System.out.println(index);
//使用指定元素,填满整个数组
Arrays.fill(nums,100);
System.out.println(Arrays.toString(nums));
//Arrays.sort(数组名): 只能对已经提前设置好排序规则的类的数组进行排序。
int[] nums2 = {2,3,4,1,5};
Arrays.sort(nums2);
System.out.println(Arrays.toString(nums2));
// 扩容: 将源数组的所有元素都拷贝到新数组的最前面,然后多余的位置是默认值, 注意:返回的是一个新的数组
int[] nums3 = Arrays.copyOf(nums2,10); // [1,2,3,4,5,0,0,0,0,0]
// 指定的长度一样,因此可以理解为,完全拷贝。新数组和源数组一模一样
int[] nums4 = Arrays.copyOf(nums2,nums.length); // [1,2,3,4,5]
// 指定的长度小于源数组的长度,可以理解为部分拷贝。注意是从头开始拷贝
int[] nums5 = Arrays.copyOf(nums2,3);
System.out.println(Arrays.toString(nums5));
}
}