Java——方法详细介绍

news2024/12/26 23:24:39

一、方法调用机制

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 和 b
    public 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 和 3
        int 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.coloradmin.cn/o/1809550.html

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

相关文章

GUI编程02-布局管理器

流式布局 FlowLayout 东西南北中 BorderLayout 表格布局 GridLayout 流式布局 package YMP.GUI; ​ import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; ​ public class TestFlowLayout {public static void main(String[] args…

oracle 12.1 rac to rac adg(maa)搭建保姆级教程

目录 资源配置 一、主库集群操作 1.主库增加standbylog 2.主库开启force logging及归档 3.主库配置参数 4.生成参数文件并将参数文件、密码文件拷贝至备库 4.1参数文件处理 4.2密码文件处理 二、备库操作 1.备库修改参数文件 1.1创建adump目录并在参数文件修改&#…

Mysql基础 - 事务

Mysql基础 - 事务 文章目录 Mysql基础 - 事务1 事务简介2 事务操作2.1 控制事务一2.2 控制事务二 3 事务四大特性4 并发事务问题5 事务隔离级别 1 事务简介 事务是一组操作的集合,他是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提…

Word中插入Mathtype右编号,调整公式与编号的位置

当你已经将mathtype内置于word后,可以使用右编号快速插入公式 但是往往会出现公式和编号出现的位置或之间的距离不合适 比如我在双栏下插入公式,会发现插入的公式与编号是适用于单栏的 解决办法: 开始->样式->MTDisplayLquation -&g…

力扣刷题--728. 自除数【简单】

题目描述 自除数 是指可以被它包含的每一位数整除的数。 例如,128 是一个 自除数 ,因为 128 % 1 0,128 % 2 0,128 % 8 0。 自除数 不允许包含 0 。 给定两个整数 left 和 right ,返回一个列表,列表的…

MySQL之查询性能优化(七)

查询性能优化 排序优化 无论如何排序都是一个成本很高的操作,所以从性能角度考虑,应尽可能避免排序或者尽可能避免对大量数据进行排序。前面已经提到了,当不能使用索引生成排序结果的时候,MySQL需要自己进行排序,如果…

02-JAVA面向对象编程

一、面向对象编程 1、面向过程编程思想(Process Oritented Programming) 将实现一个功能的一组指令组合在一起,成为一个函数。这个函数就能实现这一个功能,是对功能实现的一种抽象。通过这种抽象方式,将代码实现复用。…

Jmeter分布式、测试报告、并发数计算、插件添加方式、常用图表

Jmeter分布式 应用场景 当单个测试机无法模拟用户要求的业务场景时,可以使用多台测试机进行模拟,就是Jmeter的分布 式测试。 Jmeter分布式执行原理 Jmeter分布测试时,选择其中一台作为控制机(Controller)&#xff0c…

Python框架scrapy有什么天赋异禀

Scrapy框架与一般的爬虫代码之间有几个显著的区别,这些差异主要体现在设计模式、代码结构、执行效率以及可扩展性等方面。下面是一些关键的不同点: 结构化与模块化: Scrapy:提供了高度结构化的框架,包括定义好的Spider…

【InternLM实战营第二期笔记】07:OpenCompass :是骡子是马,拉出来溜溜

文章目录 课程实操 课程 评测的意义是什么呢?我最近也在想。看到这节开头的内容后忽然有个顿悟:如果大模型最终也会变成一种基础工具(类比软件),稳定或可预期的效果需要先于用户感知构建出来,评测 case 就…

jquery.datetimepicker无法添加清除按钮的问题

项目场景: 自从决定用现有新技术实现CRM老项目起,就开始了我的折腾之路,最近一直在折腾前端页面,不像后端Java,写的有问题运行会报错,大多数报错一搜就能找到解决方案,前端这个倒好&#xff0c…

《TCP/IP网络编程》(第十四章)多播与广播

当需要向多个用户发送多媒体信息时,如果使用TCP套接字,则需要维护与用户数量相等的套接字;如果使用之前学习的UDP,传输次数也需要和用户数量相同。 所以为了解决这些问题,可以采用多播和广播技术,这样只需要…

storage存储模块-vuex持久化处理

1:存储登录用户信息到vuex中 在store文件夹下面,创建modules文件夹在文件夹下创建user.js文件 user.js文件 const state {userInfo: {userId: ,token: } } const mutations {setUserInfo (state, obj) {console.info(obj)state.userInfo.userId obj…

字符串常量简单介绍

C/C内存四区介绍 如前文所示,字符串常量存储在静态存储区的字符串常量区,这样做的好处是 当程序使用到多个相同的字符串常量时,实际上都是使用的同一份,这样就可以减小程序的体积。注意字符串常量是只读的不能被修改。 如图所示&…

通用信息提取数据预处理

train_data./datasets/duuie output_folder./datasets/duuie_pre ignore_datasets["DUEE", "DUEE_FIN_LITE"] schema_folder./datasets/seen_schema # 对CCKS2022 竞赛数据进行预处理 import shutil # shutil.copytree(train_data,output_folder) impor…

「网络原理」三次握手四次挥手

🎇个人主页:Ice_Sugar_7 🎇所属专栏:计网 🎇欢迎点赞收藏加关注哦! 三次握手&四次挥手 🍉连接管理🍌三次握手🍌意义🍌四次挥手🍌TCP 状态转换…

电路防护-贴片陶瓷气体放电管

贴片陶瓷气体放电管 GDT工作原理GDT主要特性参数典型电路压敏电阻与 TVS 管的区别 GDT工作原理 陶瓷气体放电管是一种电子器件,其工作原理基于气体放电现象。这种管子的内部填充了一种特定的气体,通常是氖气或氩气。当管子两端施加足够的电压时&#xf…

vue3-使用富文本编辑器-wangEditor-文章发表1

最近在搞项目:我们组内几位成员正在搞一个网站搭建,以后更新会比较缓慢 引言:如果要网站要用的富文本编辑器的话,这边推荐用wangEditor 官网地址传送 : wangEditorhttps://www.wangeditor.com/ 我现在还在扩展我的写文章用的富文本编辑器 现在我将简单介绍一下其基本使用方…

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

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

在 Android App 里使用 C 代码 - NDK

原生开发套件 (NDK) 是一套工具,使能够在 Android 应用中使用 C 和 C 代码,并提供众多平台库,可使用这些平台库管理原生 activity 和访问实体设备组件,例如传感器和触控输入。 NDK 可能不适合大多数 Android 编程初学者&#xff…