【Java SE】方法的使用

简介: 可能学过C语言的小伙伴就知道,C程序是由许许多多的函数组成的,而每个函数对应着一个功能,比如很多的一些库函数,还有我们自己会写一些函数,其实在Java中,我们把函数称作方法,如果从广义上讲,就是来帮助我们解决问题的,既然是解决问题,我们也可以把所有函数都写在main函数内部啊,那如果我们其他地方也需要呢?

1、方法的简述和概念

1.1 什么是方法?

可能学过C语言的小伙伴就知道,C程序是由许许多多的函数组成的,而每个函数对应着一个功能,比如很多的一些库函数,还有我们自己会写一些函数,其实在Java中,我们把函数称作方法,如果从广义上讲,就是来帮助我们解决问题的,既然是解决问题,我们也可以把所有函数都写在main函数内部啊,那如果我们其他地方也需要呢?复制粘贴?这样太麻烦了,这里会造成大量的重复代码,增加了重复性工作,使得程序变得更繁琐,更不利于我们的维护,同时也不利于我们复用,所以为了解决这些问题,我们就出现了方法,当我们需要的时候,调用即可。

如果你还在纠结方法的好处有哪些?那请把我上面列举不使用的方法的缺点反过来,你就知道方法的好处了

这里举个例子,假设我要判断 n 的阶乘,我在main函数写了他的实现,在很下面很下面,我又需要求阶乘了,我又得去拷贝上面的求阶乘代码,还得改一下参数,这样行不行,可行!但是不方便,难不成我一个程序需要求10个数的阶乘,我有10段重复代码,那给阅读者也带来很不好的体验,接下来,我们正式进入方法的学习:

1.2 方法的定义和使用

我们就来写一个求阶乘的方法,首先我们需要知道方法的语法格式是什么:

可能看到上面这些术语的时候还有很多小伙伴不懂,什么是修饰符,这里我们需要注意如下点:

  • 修饰符:在目前我们用 public static 搭配即可,当后期讲到的时候在细说
  • 返回类型:如果没有返回值,可以必须写成 void,如果有,返回的类型必须与实体类型一致
  • 方法名称:统一采用小驼峰命名,不要使用拼英,不要过度缩写,不要使用中文!
  • 参数列表:如果不需要参数则不写,如果有参数,一定要写明参数类型,多个参数之间用逗号隔开
  • 方法体:方法需要执行的语句,遇到 return 则返回
  • 方法必须写在类中,方法不能嵌套定义,没有方法声明的说法
  • 方法只有被调用的时候才执行,结束后回到主调方法继续往下执行

当我们拿到求阶乘的值,我们首先应该思考,阶乘是指 1 * 2 * 3 .... * n,这求的就是 n 的阶乘,首先这个函数肯定有返回值,需要产生 1~n 个连续的整数,再者我们需要拿一个变量每次把乘积存起来,这个变量的初始值不能为0, 有了这种思路,就可以开始写代码了:

那么我们写这样一个方法有什么好处呢?如果我们后续代码还需要求阶乘,是不是就可以直接调用这个函数传参即可?不仅增加了代码可读性,还更利于我们的操作。

1.3 形参与实参的关系

这里如果我们要写一个方法交换两个变量的值,于是聪明的张三就赶忙敲起键盘,不到 30 秒写完了以下代码:

public class TestDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println("交换前:" + a + " " + b);
        swap(a, b);
        System.out.println("交换前:" + a + " " + b);
    }
    public static void swap(int x, int y) {
        int tmp = x;
        x = y;
        y = tmp;
    }
}

可是运行起来发现并没有交换,如果有学习过C语言的小伙伴可能就明白了,形参并不会改变实参的值,为什么呢?因为形参只是实参的一份临时拷贝,那我们传地址,拿指针变量接收,可以吗?不可以!Java中没有所谓的指针。那我们如何解决呢,这里我们先不解决,等到后面学习引用类型,问再来拿这个例子出来修改,如果现在解决了,你也会马马虎虎的弄不懂。

形参呢只是在方法定义的时候,需要借助的变量而已,本质上是用来保存方法在调用时传过来的值,在Java中,实参的值永远都是拷贝到形参当中,本质上他们是两个实体。

什么是形参和实参呢?实参就是在调用方法时方法名括号后面中的参数,形参就是来源于方法的调用,可以理解他是实参的临时拷贝,在方法调用结束,栈帧空间会销毁。

1.4 方法的返回值

  • 如果方法没有返回值,是不能拿变量去接收返回值的!也不能链式访问,否则会报错!
  • 如果方法有返回值,并且方法中有分支情况,我们需要保证每个分支对应都有返回值!

具体细节我们会随着学习的深入而感受到,这里只是让你知道如何使用,在后续的学习中,等你有了一定的代码量,这其中的理论你都能明白。

2、方法重载

2.1  为什么需要方法重载?

在C语言中,我们经常写过求两个数和的函数,通常我们会写成 int 类型参数,导致于这个函数,只能进行整数求和,如果我们要进行浮点数求和,那只能在取一个名字,改成 double 类型参数,如果我们要进行三个数的求和?那我们需要写多少个函数啊?取名字也成了一件头疼的事情,可想而知,在以后的项目中,我们碰到的情况可能会更复杂,那我们可以不可以让他们都是一个函数名呢?显然C语言中是不可以的,但是Java中有了方法重载的概念,我们往后看:

2.2 方法重载的使用

在语言中,如果我们想让相同的方法名,但是可以实现不同的功能,就可以用到方法重载了,在Java中,如果多个方法的名字相同,参数列表不同,则称之为该几种方法被重载了

这里我们就来实现一下上面说的,一个 add方法名 实现两个整型相加,两个浮点数相加,三个整型相加:

在使用方法重载的时候,我们是一定要注意几个点的:

  • 方法名必须相同
  • 返回值类型无所谓
  • 方法形参列表必须不同(顺序不同,个数不同,类型不同)

如果你让方法重载,你仅仅是因为返回值的类型不同的话,就比如你本来是 int类型 返回值,你直接改成 double 类型返回值,这样是不能构成方法重载的,那这里又有小伙伴有疑问了,那编译器怎么知道我使用的哪个方法呢?它会根据我们的实参类型进行推演,来判断我们使用的是哪个方法。具体还需要下来多多尝试写代码,才能理解更深刻。

2.3 方法的签名

在有上面的学习之后,可能很多小伙伴有疑问,我之前在C语言中写函数的时候,不能出现命名一样的函数,编译器会告诉我函数重定义了,那为什么Java中方法重载后可以同名呢?其实本质上的名字是不一样的,方法签名就是编译器编译之后修改方法的最终名字,具体的方式:方法路径全名+方法参数列表类型+返回值类型,这样才能构成完整的方法名。

public class TestDemo {
    public static void main(String[] args) {
        System.out.println(add(1, 2));
        System.out.println(add(1, 2, 3));
        System.out.println(add(1.2, 2.3));
    }
    public static int add(int x, int y) {
        return x + y;
    }
    public static int add(int x, int y, int z) {
        return x + y + z;
    }
    public static double add(double x, double y) {
        return x + y;
    }
}

如果证明我们上面的结论呢?在代码如上代码编译之后,会生成字节码文件,我们可以进入生成字节码文件(.class)所处的文件夹,从地址栏输入cmd命令进入控制台,接着执行我们 javap -v 字节码文件名 就可以查看了:

方法的特殊字符说明:

image.png

3、方法的递归

递归其实在方法执行的过程中不断的自己调用自己,既然是递归,从字面意思看,他就有两个动作,一个是递,一个是归,如果一直递的话,不往回归的话,结果可想而知,是会造成无限递下去,最终导致栈溢出,为什么一直递归下去会导致栈溢出呢?

调用方法时是在JVM栈内存上开辟空间的,方法的递归,本质也是调用方法,如果要调用方法,就会形成栈帧,栈的空间是有限的,如果你一直形成新的栈帧,总会导致栈空间耗尽的情况,这就是栈溢出。  

栈溢出错误

所以由上我们就能得出一个结论:递归肯定不能无限递归下去,一定要有一个递归终止条件,也就是递归的出口。

那么我们如何使用递归解决问题呢?

我们首先得了解方法的调用过程,这个在上面已经说过了,在者我们要找到递推公式,就好比我们求 5!一样,5!可以分为 5 * (5 - 1)!,那么 (5-1)!也就是 4!也可以分成 (4 - 1)!这样重复下去,我们也就产生了一个递推公式:N!=  N*(N-1)!

有了思路,我们如何实现代码呢?那么终止递归条件是什么呢?当我们发现 N 的值为 1 了,也就是 1!自然也是 1,这个时候我们就可以直接 return n;代码实现如下:

public class TestDemo {
    public static void main(String[] args) {
        System.out.println(factorial(5));
    }
    public static int factorial(int n) {
        if (1 == n) {
            return n;
        }
        return n * factorial(n - 1);
    }
}

为了方便大家更好的理解,我们画递归展开图,3的阶乘:

对于递归来说,需要多写代码,多做练习,像我们后续Java数据结构会用递归实现二叉树, 当然递归也不一定是最优的解决方法,就比如斐波那契数列,使用递归的话时间复杂度会很高,会出现很多冗余的运算,并没有循环来的快,这里我们就要根据实际情况来决定了。至于递归的练习题,大家可以自己去找一找,多练习练习。

本期我们只是知道方法该如何使用,还不是特别能看出来方法的重要性,等往后学习,我们就会慢慢慢把学习知识串联起来,那个时候就会明白更多,学习是循环渐进的过程,很多进步都是在写代码的途中产生的,请坚持下去。

相关文章
|
2月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
261 18
|
2月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
215 4
|
3月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
162 11
|
2月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
316 5
|
3月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
4月前
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
318 46
|
3月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
466 1
|
4月前
|
算法 Java
Java语言实现链表反转的方法
这种反转方法不需要使用额外的存储空间,因此空间复杂度为,它只需要遍历一次链表,所以时间复杂度为,其中为链表的长度。这使得这种反转链表的方法既高效又实用。
431 0
|
4月前
|
存储 Java 数据处理
Java映射操作:深入Map.getOrDefault与MapUtils方法
结合 `getOrDefault`方法的简洁性及 `MapUtils`的丰富功能,Java的映射操作变得既灵活又高效。合理地使用这些工具能够显著提高数据处理的速度和质量。开发人员可以根据具体的应用场景选择适宜的方法,以求在性能和可读性之间找到最佳平衡。
200 0
|
4月前
|
缓存 人工智能 NoSQL
Java中实现Token设置过期时间的方法
本文介绍了在Java应用中实现Token设置过期时间的多种方法,包括使用JWT和Redis缓存,并结合定时任务清理过期Token,以提升系统安全性与用户隐私保护。
485 0