Java 笔记 12:Java 方法的相关使用,方法重载、参数传递,以及递归等内容

news/2024/5/17 12:51:15

一、前言

记录时间 [2024-05-02]

系列文章简摘:
Java 笔记 01:Java 概述,MarkDown 常用语法整理
Java 笔记 02:Java 开发环境的搭建,IDEA / Notepad++ / JDK 安装及环境配置,编写第一个 Java 程序
Java 笔记 09:Java 流程控制相关,常见的三种控制结构(顺序、选择、循环)
Java 笔记 11:Java 方法相关内容,方法的设计原则,以及方法的定义和调用

更多 Java 相关文章,请参考上面专栏哦。

本文讲述 Java 方法的相关使用,包含方法重载、参数传递,以及递归等内容。同时,文章仔细分析了值传递和引用传递的区别,并使用阶乘案例分析递归与栈机制之间的联系


二、方法重载

方法重载是指在同一个类中定义多个方法,它们具有相同的名称但参数列表不同的特性。

Java 允许方法重载,这样可以提高代码的灵活性和可读性,使得同一个方法名可以根据参数的不同而执行不同的操作。


1. 方法重载规则

方法重载的规则主要涉及方法名称和参数列表。方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,则编译器报错。

以下是方法重载的规则:

  • 方法名称相同: 重载的方法必须具有相同的名称。
  • 参数列表不同
    • 参数列表必须不同,这包括参数的数量、类型或者顺序
    • 如果参数列表中的参数数量不同,或者参数类型不同,或者参数类型相同但顺序不同,都会产生方法重载。
  • 返回类型可以相同也可以不同
    • 方法重载的返回类型可以相同也可以不同,只要参数列表不同即可。
  • 参数类型的提升
    • 如果没有完全匹配的方法,Java 会尝试通过提升参数类型来匹配。
    • 参数类型的提升是指将参数转换为更大的数据类型,比如从 intdouble
  • 变长参数(可变参数)
    • 可变参数的方法与重载方法之间存在一些模糊的关系。例如,一个方法接受一个 int[] 和一个接受多个 int 参数的方法可能会导致方法重载冲突。
    • 但是,如果没有更好的匹配,Java 会优先选择非可变参数的方法。

2. 重载示例

以下是一个方法重载的示例:

在这个示例中,Calculator 类定义了三个名为 add 的方法,它们的参数列表分别为两个整数、三个整数和两个双精度浮点数。

Main 类中调用了这些重载的 add 方法,并根据传递的参数类型和数量执行了不同的方法。

public class Calculator {// 重载的方法 1:两个整数相加public int add(int a, int b) {return a + b;}// 重载的方法 2:三个整数相加public int add(int a, int b, int c) {return a + b + c;}// 重载的方法 3:两个双精度浮点数相加public double add(double a, double b) {return a + b;}
}public class Main {public static void main(String[] args) {Calculator calc = new Calculator();// 调用重载的方法 1System.out.println("Sum of 5 and 3: " + calc.add(5, 3));// 调用重载的方法 2System.out.println("Sum of 5, 3, and 2: " + calc.add(5, 3, 2));// 调用重载的方法 3System.out.println("Sum of 2.5 and 3.5: " + calc.add(2.5, 3.5));}
}

三、参数传递

1. 命令行传参

命令行传参是指在运行 Java 程序时,可以从命令行向程序传递参数。这些参数可以在程序运行时被访问和使用。

在 Java 中,命令行参数通过 main 方法的参数来接收,main 方法的参数是一个 String 数组,其中存储了命令行传递的参数。

下面是一个简单的示例,演示了如何从命令行传递参数给 Java 程序:

public class CommandLineArguments {public static void main(String[] args) {// 打印传递给程序的参数数量System.out.println("Number of command line arguments: " + args.length);// 遍历并打印每个参数for (int i = 0; i < args.length; i++) {System.out.println("Argument " + i + ": " + args[i]);}}
}

假设将上述代码保存在名为 CommandLineArguments.java 的文件中。

在命令行 CMD 中,通过以下命令编译该文件:

javac CommandLineArguments.java

编译成功后,可以使用以下命令来运行程序并传递参数:

# 如果 CommandLineArguments.java 使用了包机制
# 那么在命令行中,需要回退到 package 的最外层执行
# 例如 java com.test.method.CommandLineArguments  arg1 arg2 arg3
java CommandLineArguments arg1 arg2 arg3

在这个示例中,arg1arg2arg3 是命令行传递给程序的参数。程序将打印出传递的参数数量以及每个参数的值。


2. 可变参数

可变参数是 Java 中一种特殊的参数类型,允许方法接受可变数量的参数。在方法声明中,可变参数使用三个点 ... 来表示。

  • 它必须是方法参数列表中的最后一个参数
  • 在方法内部,可变参数被视为一个数组,允许以数组的形式访问。

以下是一个示例说明可变参数的使用:

public class VarArgsExample {// 方法使用可变参数来计算一组整数的总和public static int sum(int... numbers) {int total = 0;for (int num : numbers) {total += num;}return total;}public static void main(String[] args) {// 调用 sum 方法,传递不同数量的参数System.out.println("Sum of 1, 2, 3, 4, 5: " + sum(1, 2, 3, 4, 5));System.out.println("Sum of 10, 20, 30: " + sum(10, 20, 30));System.out.println("Sum of 2, 4, 6, 8, 10, 12: " + sum(2, 4, 6, 8, 10, 12));}
}

在这个示例中,sum 方法使用可变参数来计算一组整数的总和。

main 方法中调用了 sum 方法,传递了不同数量的参数。

sum 方法内部,参数 numbers 被视为一个数组,可以像操作普通数组一样对其进行处理。


3. 值传递

值传递是一种参数传递的方式,它是指将参数的值复制一份传递给方法或函数,而不是传递参数本身。在值传递中,被调用方法的形参接收的是参数值的副本,对形参的修改不会影响到原始参数。

在 Java 中,所有的基本数据类型(如 intdoubleboolean 等)都是通过值传递传递的。这意味着将一个基本数据类型作为参数传递给方法时,方法接收到的是参数值的副本,而不是原始参数本身。

以下是一个简单的 Java 示例说明值传递:

public class ValuePassingExample {public static void main(String[] args) {int x = 10;// 调用方法前 x 的值为 10System.out.println("Before calling modifyValue method, x = " + x);// 调用方法,传递参数值的副本modifyValue(x);// 调用方法后,x 的值仍为 10// 这说明,对形参的修改不会影响到原始参数System.out.println("After calling modifyValue method, x = " + x);}public static void modifyValue(int n) {// 修改的是形参 n 的值n = 20;// 形参的值被修改为 20System.out.println("Inside modifyValue method, n = " + n);}
}

在这个示例中,modifyValue 方法接收一个整数参数 n,并将其值修改为 20。然而,这个修改只影响到了方法内部的形参 n,并没有影响到原始参数 x

因此,main 方法中输出的 x 的值仍然是10,说明参数的值并没有被修改。


4. 引用传递

引用传递是一种参数传递的方式,它是指将参数的引用(内存地址)传递给方法或函数,而不是参数值的副本。在引用传递中,被调用方法的形参接收到的是原始参数的引用,这意味着对形参的修改会影响到原始参数

在 Java 中,所有的对象引用都是通过引用传递传递的。这意味着将一个对象作为参数传递给方法时,方法接收到的是对象的引用,而不是对象本身(详见后面易混淆点)。因此,对对象的任何修改都会影响到原始对象

以下是一个简单的 Java 示例说明引用传递:

public class ReferencePassingExample {public static void main(String[] args) {StringBuilder str = new StringBuilder("Hello");// 调用方法前,str 实例对象的值为 HelloSystem.out.println("Before use method, str = " + str);// 调用方法,传递对象的引用modifyStringBuilder(str);// 调用方法前,str 实例对象的值为 Hello World// 说明,对对象的任何修改都会影响到原始对象System.out.println("After use method, str = " + str);}public static void modifyStringBuilder(StringBuilder s) {// 修改对象的内容,在字符串后面追加s.append(" World"); System.out.println("Inside modify method, s = " + s);}
}

在这个示例中,modifyStringBuilder 方法接收一个 StringBuilder 对象的引用,并将其内容追加了 " World"。这个修改影响到了原始对象 str,因此在 main 方法中输出的 str 的值是 “Hello World”。这说明对对象的修改是被传递的,因为参数是对象的引用,而不是对象本身的副本。


5. 易混淆点

首先明确一点:Java 中的所有参数传递都是按值传递的,包括对象引用。

关于对象引用:

对象引用传递给方法的是对象的内存地址,而不是对象本身。

这是因为在 Java 中,对象引用本质上是指向内存中对象的地址,而不是对象本身。

重点:地址!!!

通俗得说,就是比如,有一个 int 类型的变量 a,那这个 a 同时拥有地址和内容

  • a 的地址:在内存中的位置
  • a 的内容:100

那么通过类比,对象的地址,就好比它在内存中的位置。

将一个对象引用作为参数传递给方法时,实际上是将对象的地址传递给了方法。

这意味着方法中的形参和原始对象引用都指向了相同的对象


当创建一个对象时,实际上是在内存中分配了一块空间,并返回一个指向这块空间的引用。如果将这个引用同时赋值给另一个变量,那么这两个变量就指向了同一个对象,即它们都引用了相同的内存地址。因此,当通过一个引用修改对象时,另一个引用也会得到这个修改。

就好比有两个数组 str1 和 str2,str1 == str2 并不是它们的内容相同,而是指向的地址相同。换句话说就是,它们其实指向同一个数组,只要修改其中一个,另一个也会被修改。

因此,在方法内部通过形参修改对象时,实际上是通过相同的引用修改了原始对象。

结论:

尽管 Java 中的参数传递是按值传递的,但当传递的值是对象引用时,对于对象的修改会影响到原始对象。


四、递归

1. 使用递归

递归是指一个方法在内部调用自身的过程,也就是方法自己调用自己。

利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的能力在于用有限的语句来定义对象的无限集合。

在编程中,递归是一种解决问题的方法,其中问题的解决方案依赖于解决相同问题的小规模实例的解决方案。

在 Java 中,递归函数包括两个主要部分:

  • 基本情况(递归头):基本情况是递归算法终止的条件。如果满足了基本情况,递归函数将不再调用自身,而是返回一个确定的值。
  • 递归情况(递归体):递归情况是指递归函数调用自身的情况。在递归情况中,问题被分解为更小的相似问题,然后通过递归调用解决这些子问题。

2. 递归实现阶乘

阶乘概念

阶乘是一个数学概念,阶乘在组合数学和概率统计中有广泛的应用,例如排列和组合的计算,以及概率问题中的排列组合计数。

阶乘表示从 1 到给定整数之间所有整数的乘积。通常用符号 ! 表示。

例如,阶乘 5,写作 5!,表示从 1 到 5 之间所有整数的乘积,即:
5 ! = 5 × 4 × 3 × 2 × 1 = 120 5!=5×4×3×2×1=120 5!=5×4×3×2×1=120
阶乘的定义包括以下几点:

  1. 基本情况:0 的阶乘定义为 1。因为乘法中的乘数是一个数和它之前的所有数的乘积,如果没有任何数,则乘积为 1。
  2. 递归情况:对于正整数 n,n 的阶乘表示 n 乘以 (n-1) 的阶乘。也就是说,

n ! = n × ( n − 1 ) ! n!=n×(n-1)! n!=n×(n1)!


阶乘实现

以下是一个经典的递归示例,计算阶乘的函数:

在编程中,计算阶乘也是一个常见的任务,特别是在递归算法中。

public class Factorial {// 计算阶乘的递归函数public static int factorial(int n) {// 基本情况:当 n 等于 0 或 1 时,返回 1if (n == 0 || n == 1) {return 1;}// 递归情况:返回 n 乘以 (n-1) 的阶乘// 就是在进行阶乘操作 n(n-1)(n-2)...1return n * factorial(n - 1);}public static void main(String[] args) {int n = 5;System.out.println("Factorial of " + n + ": " + factorial(n));}
}

在这个示例中,factorial 方法是一个递归函数,它计算一个整数的阶乘。

在方法的基本情况中,当参数 n 等于 0 或 1 时,返回 1;在递归情况中,返回 n 乘以 factorial(n - 1) 的结果。

通过递归调用,这个方法会将问题不断分解,直到满足基本情况为止。


原理分析

如图所示,当使用递归方式计算阶乘时,每次递归调用都会在内存中创建一个新的栈帧,用于存储方法的局部变量、参数和返回地址。这些栈帧被按照先进后出的顺序排列在栈中。

  • 首先,在 main 方法调用 factorial(5) 时,会创建一个栈帧用于存储参数 n 的值为 5,然后调用 factorial(4)
  • factorial(4) 中,又会创建一个栈帧用于存储参数 n 的值为 4,然后调用 factorial(3),以此类推,直到递归到达基本情况。
  • 一旦递归到达基本情况(n=1),开始从栈顶逐步弹出栈帧,并计算阶乘的结果。
  • 每个弹出的栈帧都包含了一个部分阶乘的结果,并且返回到上一个调用点,直到最终的阶乘结果返回到 main 方法中。

在这里插入图片描述


3. 递归与栈机制

递归和栈机制之间有着密切的关系,因为递归函数的调用过程实际上就是通过栈来管理的。

递归调用过程

  • 当一个方法被递归调用时,当前方法的执行状态(包括局部变量、参数等)被保存在栈中的帧 frame 中。
  • 每次递归调用都会创建一个新的帧,它包含了该次调用的参数和局部变量。这些帧按照先进后出 FILO 的顺序存储在栈中。
  • 当递归到达基本情况时,也就是递归的终止条件,开始从栈中逐步弹出帧,依次执行每个帧中的代码,直到回到最初的调用点

栈溢出

  • 递归调用会导致栈的不断增长,如果递归深度过大,超出了栈的容量,就会发生栈溢出 Stack Overflow 错误。
  • 为了避免栈溢出,可以通过优化递归算法,尽量减少递归深度,或者使用迭代替代递归。

案例分析

下面是一个简单的示例,说明递归调用过程中栈的使用:

public class StackExample {public static void main(String[] args) {recursiveMethod(3);}public static void recursiveMethod(int n) {if (n <= 0) {System.out.println("Reached the base case");} else {System.out.println("Entering recursiveMethod(" + (n - 1) + ")");recursiveMethod(n - 1);System.out.println("Exiting recursiveMethod(" + (n - 1) + ")");}}
}

在这个示例中,recursiveMethod 方法被递归调用三次,每次递归调用都会创建一个新的帧,保存了当前方法的执行状态。

当递归到达基本情况时,开始从栈中逐步弹出帧,依次执行每个帧中的代码。


五、总结

本文讲述 Java 方法的相关使用,包含方法重载、参数传递,以及递归等内容。同时,文章仔细分析了值传递和引用传递的区别,并使用阶乘案例分析递归与栈机制之间的联系


一些参考资料

狂神说 Java 零基础:https://www.bilibili.com/video/BV12J41137hu/
TIOBE 编程语言走势: https://www.tiobe.com/tiobe-index/
Typora 官网:https://www.typoraio.cn/
Oracle 官网:https://www.oracle.com/
Notepad++ 下载地址:https://notepad-plus.en.softonic.com/
IDEA 官网:https://www.jetbrains.com.cn/idea/
Java 开发手册:https://developer.aliyun.com/ebook/394
Java 8 帮助文档:https://docs.oracle.com/javase/8/docs/api/


http://www.mrgr.cn/p/31203081

相关文章

【STM32+HAL+Proteus】系列学习教程4---GPIO输入模式(独立按键)

实现目标 1、掌握GPIO 输入模式控制 2、学会STM32CubeMX配置GPIO的输入模式 3、具体目标&#xff1a;1、按键K1按下&#xff0c;LED1点亮&#xff1b;2、按键K2按下&#xff0c;LED1熄灭&#xff1b;2、按键K3按下&#xff0c;LED2状态取反&#xff1b; 一、STM32 GPIO 输入…

db.create_all() 报错上下文?flask_sqlalchemy创建数据库找不到上下文?

问题报错: RuntimeError: Working outside of application context.This typically means that you attempted to use functionality that needed the current application. To solve this, set up an application context with app.app_context(). See the documentation for…

CUDA内存模型

核函数性能并不只与线程束的执行有关。 CUDA内存模型概述 GPU和CPU内存模型的主要区别是&#xff0c;CUDA编程模型能将内存层次结构更好地呈现给用户&#xff0c;能让我们显示的控制它的行为。 对程序员来说&#xff0c;一般有两种类型的存储器&#xff1a; 可编程的&#x…

csrf-基于Pikachu的学习

CSRF-跨站请求伪造 CSRF的原理 CSRF攻击即Cross-site request forgery,跨站请求伪造,直白来说就是恶意网站伪装成用户,向被害网站发起操作请求。用户输入账号信息请求登录A网站。 A网站验证用户信息,通过验证后返回给用户一个cookie 在未退出网站A之前,在同一浏览器中请求…

LeetCode 543.二叉树的直径

题目描述 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5]…

[论文笔记]SEARCHING FOR ACTIVATION FUNCTIONS

引言 今天带来另一篇激活函数论文SEARCHING FOR ACTIVATION FUNCTIONS的笔记。 作者利用自动搜索技术来发现新的激活函数。通过结合详尽的搜索和基于强化学习的搜索&#xff0c;通过实验发现最佳的激活函数 f ( x ) x ⋅ sigmoid ( β x ) f(x) x \cdot \text{sigmoid}(βx…

排序-八大排序FollowUp

FollowUp 1.插入排序 (1).直接插入排序 时间复杂度:最坏情况下:0(n^2) 最好情况下:0(n)当数据越有序 排序越快 适用于: 待排序序列 已经基本上趋于有序了! 空间复杂度:0(1) 稳定性:稳定的 public static void insertSort(int[] array){for (int i 1; i < array.length; i…

项目运行到手机端

运行到真机 手机和点到连在同一个wifi网络下面点击hbuiler上面的预览得到一个&#xff0c;network的网址这个时候去在手机访问&#xff0c;那么就可以访问网页了 跨域处理 这个时候可能会访问存在跨域问题 将uniapp的H5版本运行到真机进行调试&#xff0c;主要涉及到跨域问题…

Ubuntu 24.04 LTS (Noble Numbat) 正式版发布

Ubuntu 24.04 LTS (Noble Numbat) 正式版发布 Canonical 的第 10 个长期支持版本在性能工程、企业安全和开发人员体验方面树立了新标准 请访问原文链接&#xff1a;Ubuntu 24.04 LTS (Noble Numbat) 正式版发布&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。…

数组模拟双链表-java

通过数组来模拟双链表&#xff0c;并执行一些插入和删除的功能。 目录 一、问题描述 二、模拟思路 1.变量解释 2.数组初始化 3.在下标是k的结点后面插入一个结点 4.删除下标为k的结点 5.基本功能解释 三、代码如下 1.代码如下&#xff1a; 2.读入数据&#xff1a; 3…

Java设计模式 _结构型模式_桥接模式

一、桥接模式 1、桥接模式 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式。用于把一个类中多个维度的抽象化与实现化解耦&#xff0c;使得二者可以独立变化。 2、实现思路 使用桥接模式&#xff0c;一定要找到这个类中两个变化的维度&#xff1a;如支…

使用LinkAI创建AI智能体,并快速接入到微信/企微/公众号/钉钉/飞书..

LinkAI 作为企业级一站式AI Agent 智能体搭建与接入平台,可以使用LinkAI创建专属智能体,并将它连接到并快速接入到微信/企微/公众号/钉钉/飞书/web等​ LinkAI 作为企业级一站式AI Agent 智能体搭建与接入平台,不仅为用户和客户提供能够快速搭建具备行业知识和个性化设定的 …

基于CodeMirror开发在线编辑器时遇到的问题及解决方案

需求:实现json在线编辑并支持校验,基于此使用了 CodeMirror在线编辑,jsonlint校验输入数据 // package.json:"dependencies": {"codemirror": "^5.53.2","core-js": "^3.8.3","jsonlint": "^1.6.3",…

【VMware vCenter】连接和使用vCenter Server嵌入式vPostgres数据库。

vCenter Server 早期支持内嵌(embedded)和外部(external)数据库,内嵌数据库就是vPostgres,基于VMware Postgres数据库(PostgreSQL数据库),外部数据库用的多的是Oracle数据库和SQL Server数据库。因为早期使用内嵌的PostgreSQL数据库只能用于小型环境,比如仅支持几十台…

对于 CF1107E 中 dp 状态设计的一点想法

我在这延伸谱线誊写勾勒 / 试图将歌唱的意义勘破不太想发到洛谷讨论区,就往这里放了。 我觉得现有的题解都没说明白为什么本题的状态和转移能覆盖所有情况,并且感觉也非常不自然,没见过的话感觉挺难发现这么一个东西。 然而这个 dp 其实是可以自然地推导出来的。 首先发现这…

线性代数-行列式-p1 矩阵的秩

目录 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质 1.定义 2. 计算矩阵的秩 3. 矩阵的秩性质 4. 自己补充点

网络应用层之(6)L2TP协议详解

网络应用层之(6)L2TP协议 Author: Once Day Date: 2024年5月1日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文档可参考专栏&#xff1a;通信网络技术_Once-Day的…

解决vscode连接远程服务器出现Bad owner or permissions on C:\\Users\\Administrator/.ssh/config 过程试图写入的管道不存在。

1.找到.ssh文件夹。它通常位于C:\Users2.右键单击.ssh文件夹,然后单击“属性”,选择“安全”3.单击“高级”。 单击“禁用继承”,单击“确定”。 将出现警告弹出窗口。单击“从此对象中删除所有继承的权限”。 4.此时所有用户都将被删除。添加所有者。在同一窗口中,单击“编…

TCP的三次握手过程

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的...TCP是面向连接的、可靠的、基于字节流的传输层通信协议。 TCP是面向连接的协议,所以使用 TCP前必须先建立连接,而建立连接…

C++成员初始化列表

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升&#xff0c;那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题 文章目录 引入成员初始化列表用成员初始化列表优化上面的代码成员初始化列表展开成员初始化列表的潜在危险 参考资料 引入 考虑下面这…