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

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

相关文章
|
3天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
27 4
|
14天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
35 17
|
7天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
13 2
|
15天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
13 3
|
15天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
12 2
|
15天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
12 1
|
15天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
26 1
|
15天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
22 1
|
15天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
26 1
|
9天前
|
Java Spring
JAVA获取重定向地址URL的两种方法
【10月更文挑战第17天】本文介绍了两种在Java中获取HTTP响应头中的Location字段的方法:一种是使用HttpURLConnection,另一种是使用Spring的RestTemplate。通过设置连接超时和禁用自动重定向,确保请求按预期执行。此外,还提供了一个自定义的`NoRedirectSimpleClientHttpRequestFactory`类,用于禁用RestTemplate的自动重定向功能。