Java——方法详细介绍

一、方法调用机制

1、方法调用机制详细介绍

下面对方法调用在内存中的情况进行分析,以下面的代码为例:

public class Test {public static void main(String[] args) {Person person = new Person();person.name = "张三";person.age = 18;int res = person.calcSum(1,2);}
}class Person {String name;int age;//求两个数字的和的方法public int calcSum(int num1,int num2) {return (num1 + num2);}
}

在执行语句

Person person = new Person();

时,会在堆区创建一个对象,然后在 main 函数的栈帧中会创建一个对象引用变量,这个引用变量中存储的引用就是刚才创建的对象的引用。

然后执行下面这两句代码,

person.name = "张三";
person.age = 18;

对对象的属性初始化。

然后执行下面的代码,

int res = person.calcSum(1,2);

这里在调用对象 person 的方法时,会再开辟一个栈帧,就是 calSum 的栈帧,

方法内的局部变量和方法的参数在被调用时存储在方法的栈帧中。

这里的参数是基本数据类型,所以传参进行的是值拷贝,将实际参数的值直接拷贝给在 calSum 栈帧中的形式参数变量,然后进行计算,最后返回值,在方法返回后,它的栈帧就会被销毁,将返回值赋给变量 res。

这里我们发现在调用一个方法时会开辟方法对应的栈帧,再这个方法返回时,它的栈帧就会被销毁。

最后当程序退出时,main 函数栈帧也会被销毁。

二、方法调用细节

方法内部不能再定义方法。

1、访问修饰符

类的属性有访问修饰符,类的成员方法也有访问修饰符,与类的属性一致。

访问修饰符:

在Java中,访问修饰符(Access Modifiers)用于控制类、方法和属性(字段)的可见性。它们决定了其他类是否能够访问特定的类成员(属性或方法)。Java中有四种主要的访问修饰符:

  1. public
  2. protected
  3. (什么都不写时的访问级别为默认)
  4. private
修饰符当前类同一个包子类(不同包)其他包
public
protected×
默认××
private×××

2、方法返回值

返回数据类型

如果方法声明中有返回数据类型,则方法体中的执行语句必须有 return 语句。return 语句用来返回与返回值类型一致或兼容(可以自动类型转换)的值。

无返回值类型

如果方法返回值类型是 void,则方法体中可以有 return 语句,就像下面这样:

return;

只写一个 return ,不返回任何值。或者可以没有 return 语句。void 表示方法不返回任何值。

3、每个方法只能返回一个值

每个方法只能返回一个值,那如果要返回多个值怎么办呢,就可以使用以下方法:

    public int[] example(int num1,int num2) {//...int[] arr = new int[2];//...return arr;}

这样在堆区创建一个数组对象,然后返回其引用,就可以实现返回的引用可以访问一个数组,就可以通过这个数组存储多个值了。

对于这里所说的返回数组类型,实际上返回的不是一整个数组,而是数组的引用。

所以在我们调用这个方法时,也要使用数组引用变量来接受其返回值。

int[] arr = example(1,2);

4、形参和实参

形参是指在方法声明中定义的参数,它们作为占位符,用于接收调用者传递的实际参数值。

  1. 定义位置:形参定义在方法的参数列表中。
  2. 作用范围:形参的作用范围仅限于方法内部。
  3. 数据类型:形参需要指定类型,可以是基本数据类型或引用数据类型。
public class Example {// 形参是 a 和 bpublic int add(int a, int b) {return a + b;}
}

在上述示例中,ab 是形参。

实参是指在方法调用时传递给方法的实际值或对象,这些实际值或对象被传递给形参。

  1. 定义位置:实参在方法调用时提供。
  2. 传递方式:实参的值或引用被复制给形参,在方法执行过程中使用。
  3. 类型匹配:实参的类型必须与形参的类型兼容。
public class Test {public static void main(String[] args) {Example example = new Example();// 实参是 5 和 3int result = example.add(5, 3);System.out.println("Result: " + result);}
}

在上述示例中,53 是实参。

在方法调用时,实参的类型必须与形参的类型匹配或兼容。例如,如果形参是 int 类型的,实参也必须是 int 类型或可以隐式转换为 int 类型。

三、方法传参机制

上面我们提到方的形参和实参,下面我们介绍一下方法的传参机制。Java中的方法参数传递机制是值传递(Pass-by-Value),对于基本类型的参数,传递的是数据的副本;对于引用类型的参数,传递的是对象的引用副本。

1、基本数据类型传参

下面我们看一个经典的例子:

public class Test {public static void swap(int a, int b) {int temp = a;a = b;b = temp;}public static void main(String[] args) {int num1 = 1;int num2 = 2;swap(num1,num2);System.out.println("num1 = " + num1 + "\nnum2 = " + num2);}
}

运行这段代码,运行结果为:

可以发现这里两个变量的值并未完成交换。这是为什么呢?

在主函数中调用 swap 函数时,会开辟 swap 函数的栈帧,然后形参变量会存储在 swap 函数的栈帧中,然后会将主函数中的两个变量的值拷贝(赋值)给 swap 函数栈帧中的两个形参变量。

然后会进行 swap 函数栈帧中的两个形参变量的值的交换,这个操作不会影响 main 函数栈帧中的两个实参变量,因为 swap 栈帧中的两个形参变量和 main 函数栈帧中的两个实参变量是相互独立的变量。

然后 swap 函数的语句执行完成后,swap 栈帧会被销毁,两个 swap 中的形参变量也被销毁。

这时 main 函数中的两个实参变量没有受到任何影响,所以还是原样,所以实参的值没有被交换。

所以说对于基本数据类型(如 intfloatchar 等),传递的是值的副本。方法内部对形参的修改不会影响实际参数的值。因为形参和实参是两个独立的变量,修改其中一个变量的值对另一个变量没有影响。

例如:

public class ValuePassing {public static void modifyPrimitive(int number) {number = 10;}public static void main(String[] args) {int original = 5;modifyPrimitive(original);System.out.println("Original value after method call: " + original);}
}

输出结果:

Original value after method call: 5

在这个示例中,original 的值被复制给 number,方法内部对 number 的修改不会影响 original 的值。originalnumber 是两个相互独立的变量,original 在 main 函数栈帧中,number 在 modifyPrimitive 函数栈帧中。修改其中一个变量的值不影响另一个变量。

2、引用类型传参

下面我们看一个例子:

public class Test {public static void modify(Person person) {person.age = 0;}public static void main(String[] args) {Person student = new Person();student.age = 19;modify(student);System.out.println(student.age);}
}class Person {String name;int age;}

我们运行这段代码,运行结果为:

这里为什么可以在方法内对数据进行改动呢?下面我们详细分析:

首先在主函数中创建了一个 Person 类的对象引用变量 student,然后再堆区中创建对象,将对象的引用返回给对象引用变量。

然后将 student 的 age 属性更改为了 19。

然后就调用了 modify 函数,这时 modify 的形参 person 就会存储在 modify 函数栈帧中,

然后实参 student 就会将其中村村的引用的副本赋值给形参 person,也就是将图中的 0xffff0011 赋值给形参,然后形参中存储的引用就也是 0xffff0011 了,这时使用形参也能访问到 student 这个对象了。

然后 modify 函数中的 person.age = 0; 语句就会访问 student 对象,然后修改其 age 属性。这时 student.age 就被修改成 0 了。

这里在方法内部使用对象引用变量 person 存储的引用访问对应的对象,然后改动对象的属性,实参引用的对象的属性也发生了变化,是因为这里的形参中的引用与实参中的引用是指向同一个对象的引用。

如果形参中的引用改变了的话,就没办法改动实参引用的对象的属性了。详细看下面的这个例子。

下面再看另一个例子:

public class Test {public static void reassign(Person person) {person = new Person();person.age = 18;}public static void main(String[] args) {Person student = new Person();student.age = 20;reassign(student);System.out.println(student.age);}
}class Person {String name;int age;}

运行结果:

下面我们进行详细分析:

首先在主函数中创建了一个 Person 类的对象引用变量,然后在堆区中创建对象 student,将对象的引用返回给对象引用变量。

Person student = new Person();
student.age = 20;

然后将 student 对象的 age 属性改为 19。

然后调用 reassign 函数,会开辟相应的函数栈帧,这时形参会被实参的拷贝赋值,实参是对象引用变量 student,实参中存储的是一个对象的引用,然后将这个引用赋值给形参。这时形参也指向 student 对象,形参也可以访问 student 对象。

然后 reassign 函数又在堆区中创建类一个 Person 类对象,

person = new Person();
person.age = 18;

将对象的引用返回给形参 person,这时 person 中存储的就是这个新创建的对象的引用了,person 就不能访问 student 对象了。

然后将这个新创建的对象的 age 属性改为18。

然后 reassign 函数语句执行完毕,reassign 函数栈帧就会被销毁,然后那个新创建的对象也因为失去被引用的机会而被垃圾回收机制回收。

这时我们发现 student 对象的 age 属性并没有什么改变。这是因为在 reassign 函数内对形参重新分配一个对象的引用对实参是没有影响的,因为形参和实参是两个相互独立的对象引用变量。

由此我们知道,对于引用数据类型(如对象和数组),传递的是对象的引用的副本。如果形参和实参存储的引用是同一个的话,方法内部对形参引用的对象进行修改会影响实际参数引用的对象,但对形参重新分配引用不会影响实际参数存储的引用。因为形参和实参是两个独立的对象引用变量。

四、方法调用实例

在了解到了上面这么多方法的使用注意事项后,我们可以实现一些实例,加增加方法使用的熟练度。

1、克隆对象

public class Test {public static Person copyPerson(Person person) {Person newPerson = new Person();//在堆区创建一个新的对象newPerson.name = person.name;//将原来的对象的属性赋值给新的对象newPerson.age = person.age;return newPerson;//返回新对象的引用}public static void main(String[] args) {Person p = new Person();p.name = "张三";p.age = 19;Person p1 = copyPerson(p);//调用对象拷贝方法System.out.println(p1.name + " " + p1.age);System.out.println("两个对象是否是同一个 " + (p == p1));//通过比较引用确定两个对象是否相等}
}class Person {String name;int age;
}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/25817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux文件篇】优化文件读写,加速数据处理策略——缓冲区

W...Y的主页 😊 代码仓库分享 💕 前言:我们已经复习了C语言中的接口,并且学习了许多文件系统调用,了解了文件描述符以及重定向。今天我们继续学习文件缓冲区的相关内容。 缓冲区 在学习C语言时,我们经常…

LabVIEW电池测试系统

1. 背景 随着电动汽车、可再生能源等领域的迅速发展,电池作为能源储存和释放的核心组件,其性能评估变得尤为重要。电池的充放电性能、容量、循环寿命等参数直接影响着设备的工作性能和使用寿命。因此,设计一套全面、准确的电池测试系统对于提…

技术前沿 |【大模型BLIP-2的多模态训练】

大模型BLIP-2的多模态训练 一、引言二、BLIP-2模型概述三、多模态训练成本问题四、冻结预训练好的视觉语言模型参数的优势五、冻结预训练好的视觉语言模型参数的方法 一、引言 随着人工智能技术的飞速发展,大型多模态模型如BLIP-2在多个领域取得了显著的成果。然而…

学生信息管理(C语言)

学生信息管理 (1)问题描述 学生信息包括:学号,姓名,年龄,性别,出生年月,地址,电话,E-mail等。试设计一学生信息管理系统,使之能提供以下功能&am…

[图解]建模相关的基础知识-07

1 00:00:04,710 --> 00:00:08,900 这是划分,下一个是有序对的概念 2 00:00:11,720 --> 00:00:13,800 我们知道集合是不分顺序的 3 00:00:15,090 --> 00:00:18,200 我们花括号来代表集合的话 4 00:00:18,210 --> 00:00:21,000 AB花括号等于BA花括号 …

2_1 Linux基础操作

2_1 Linux基础操作 文章目录 2_1 Linux基础操作0. 参考1. 装机后的一些小命令查看系统的信息2. 基础命令2.1 初识基本命令2.2 日期和时间 3. 帮助命令4. 关机、重启5. 设置主机名6. rm删除7. 软件包的管理RPM、 YUM8. IP知识9. 查看一些linux的信息10. 命令行快捷键11. 光盘挂载…

数据可视化——pyecharts库绘图

目录 官方文档 使用说明: 点击基本图表 可以点击你想要的图表 安装: 一些例图: 柱状图: 效果: 折线图: 效果: 环形图: 效果: 南丁格尔图(玫瑰图&am…

贪心算法06(leetcode738,968)

参考资料&#xff1a; https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html 738. 单调递增的数字 题目描述&#xff1a; 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。…

CTF-PWN-kernel-UAF

文章目录 参考slub 分配器kmem_cache_cpukmem_cache_node[ ]冻结和解冻分配释放 fork绑核Kmalloc flag和slub隔离CISCN - 2017 - babydriver检查babtdriver_initstruct cdevalloc_chrdev_regioncdev_initownercdev_add_class_createdevice_create babyopenbabyreleasebabyreadb…

【C++】STL中list的使用

前言&#xff1a;在前面学习的 过程中我们学习了STL中的string,vector&#xff0c;今天我们来进一步的学习STL中的list的使用方法。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习 &#x1f448; &#x1f4af;代…

全志D1s软件入门之Tina Linux烧写教程

烧写 Tina Linux 烧写&#xff0c;即将编译打包好的固件下载到设备 烧写方式简介 全志平台为开发者提供了多种多样的烧写方式和烧写工具&#xff1a; &#xff08;1&#xff09; PhoenixSuit&#xff1a;基于Windows的系统的烧写工具&#xff0c;是最常用的烧写工具&#x…

树莓派4b安装宝塔面板

1、打开命令窗口&#xff0c;执行如下命令 #更新 sudo apt-get update sudo apt-get upgrade #切换root权限 sudo su root #安装宝塔面板 wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && bash install.sh安装过程有点久&#xff0c;会持…

01——生产监控平台——WPF

生产监控平台—— 一、介绍 VS2022 .net core(net6版本&#xff09; 1、文件夹&#xff1a;MVVM /静态资源&#xff08;图片、字体等&#xff09; 、用户空间、资源字典等。 2、图片资源库&#xff1a; https://www.iconfont.cn/ ; 1.资源字典Dictionary 1、…

C++系统编程篇——linux编译器 gcc/g++(链接动静态库)

linux编译器-gcc/g &#xff08;1&#xff09;g安装&#xff08;gcc一般自带&#xff0c;g需要下载&#xff09; sudo yum install -y gcc-c g --version gcc用于编译C语言代码&#xff0c;g用于编译C代码 &#xff08;2&#xff09;程序翻译过程 选项“-o”是指目标文件…

Python的return和yield,哪个是你的菜?

目录 1、return基础介绍 &#x1f4da; 1.1 return用途&#xff1a;数据返回 1.2 return执行&#xff1a;函数终止 1.3 return深入&#xff1a;无返回值情况 2、yield核心概念 &#x1f347; 2.1 yield与迭代器 2.2 生成器函数构建 2.3 yield的暂停与续行特性 3、retur…

MFC 教程-回车时窗口退出问题

【问题描述】 MFC窗口默认时&#xff0c;按回车窗口会退出 【原因分析】 默认调用OnOK() 【解决办法】 重写虚函PreTranslateMessage BOOL CTESTMFCDlg::PreTranslateMessage(MSG* pMsg) {// TODO: 在此添加专用代码和/或调用基类// 修改回车键的操作反应 if (pMsg->…

C++11新特性【上】(统一的列表初始化、auto、decltype、右值引用、万能引用、完美转发)

一、C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了 C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞 进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性的把两个标准合…

高德地图简单实现点标,和区域绘制

高德地图开发文档:https://lbs.amap.com/api/javascript-api/guide/abc/quickstart 百度搜索高德地图开发平台 注册高德地图开发账号 在应用管理中 我的应用中 添加一个Key 点击提交 进入高德地图开发文档:https://lbs.amap.com/api/javascript-api/guide/abc/quickstart …

Mysql 的分布式策略

1. 前言 MySQL 作为最最常用的数据库&#xff0c;了解 Mysql 的分布式策略对于掌握 MySQL 的高性能使用方法和更安全的储存方式有非常重要的作用。 它同时也是面试中最最常问的考点&#xff0c;我们这里就简单总结下 Mysq 的常用分布式策略。 2. 复制 复制主要有主主复制和…

使用 C# 学习面向对象编程:第 2 部分

C# 类属性简介 属性在面向对象编程中起着至关重要的作用。它们允许我们从类外部访问类的私有变量。在类中使用私有变量是很好的。属性看起来像变量和方法的组合。属性有部分&#xff1a;“get 和 set”方法。get 方法应该返回变量&#xff0c;而 set 方法应该为其赋值。 步骤…