当前位置: 首页 > news >正文

Java参数传递

Java参数传递

一、 方法重载

一个类中可以存在多个同名的方法,只要这些方法的参数列表不同即可。

  • 参数列表不同:参数个数或者参数类型不同
  • 方法重载与修饰符、返回值类型等统统无关,只看参数列表

二、 可变个数的形参

从Java5.0开始,Java有了可变形参的语法,即形参的类型可以确定,但是形参的个数不确定

可变形参的格式:方法名(参数类型... 参数名)

比如:

public void f(int i, String... strings) {for (String string : strings) {System.out.println(string);}
}

此时,调用方法f时,可以传入1个或多个字符串,甚至可以不传入字符串。比如:

f(0); // 不传入字符串
f(1, "one"); // 传入1个字符串
f(2, "one", "two"); //入多个字符串

注:

  • 可变形参等价于使用数组形参,因此当两个方法一个使用可变形参、一个使用数组且其他参数列表相同时,将会报错。比如下面的语句将会报错:

    public void f(int i, String... strings) {}public void f(int i, String[] strings) {}
    
  • 可变形参需要放在方法参数列表的最后

  • 在一个方法中,最多只能声明一个可变形参

三、 值传递

3.1 两种传递方式

参数传递有引用传递以及值传递两种方式:

  • 引用传递,在调用方法时将参数的地址直接传递到方法中
  • 值传递,在调用方法时将参数的值拷贝一份传递到方法中

3.2 Java参数传递类型

Java在传递参数时,只有值传递这一种方式。从JVM层面来看,Java在传递参数时是将栈中变量的值拷贝一份传递给参数。

3.2.1 传递基本类型

当传递基本类型时,将基本类型的数值直接拷贝一份传递给方法,在方法内部对参数做的修改并不会影响到方法外部。比如:

public class Main {public static void main(String[] args) {int i = 10;System.out.println("调用 test 方法前 i 的值:" + i);test(i);System.out.println("调用 test 方法后 i 的值:" + i); // test 方法并不会修改 i 的值}public static void test(int m) {m = 20;}
}

调用test方法时,main方法将整形变量i的值10拷贝赋值给参数m(这相当于初始化test方法内部的变量m),在test内部对变量m的改动并不会影响main方法中的变量i

3.2.2 传递引用类型

引用类型与基本类型在内存中的存储方式略有不同:

  • 基本类型在栈中存放的是变量具体的值
  • 引用类型在栈中存放了一个地址,该地址指向堆中的某块内存区域,而该内存区域才是真正存放对象的地方
public class Main {public static void main(String[] args) {int i = 0;User user = new User(16, "main");}
}class User {public int age;public String name;public User(int age, String name) {this.age = age;this.name = name;}
}

比如,上面这段代码,main方法中有两个局部变量,一个是整形变量i,一个是引用类型user,这两个变量在内存中的分布如下图所示:

请添加图片描述

引用类型的这种存储特点使得其在参数传递时产生了一些副作用:

  1. 与传递基本类型一样,当传递引用类型时,将引用类型的数值直接拷贝一份传递给方法,此时不会影响到存放在栈中的值。比如:

    public class Main {public static void main(String[] args) {User user = new User(16, "main");System.out.println("调用 f1 方法前 user 的值:" + user);f1(user);System.out.println("调用 f1 方法后 user 的值:" + user); // user的值不会发生改变}public static void f1(User userArg) {userArg = new User(20, "f2");System.out.println("f1:" + userArg);}
    }class User {public int age;public String name;public User(int age, String name) {this.age = age;this.name = name;}
    }
    

    从输出结果可以看到,与传递基本类型一样,在调用f1方法前后user的值不会发生改变。

  2. 虽然不会影响到存放在栈中的值,但是会影响到堆中的值。比如:

    public class Main {public static void main(String[] args) {User user = new User(16, "main");System.out.println("调用 f2 方法前 user 的值:" + user.age + user.name);f2(user);System.out.println("调用 f2 方法后 user 的值:" + user.age + user.name); // user所指向的对象发生了改变}public static void f2(User userArg) {userArg.age = 18;userArg.name = "f2";}
    }class User {public int age;public String name;public User(int age, String name) {this.age = age;this.name = name;}
    }
    

    从输出结果可以看到,在调用f2方法后user对象的成员变量值发生了改变。这是因为虽然值传递是拷贝,但是useruserArg指向的地址是同一个,所以方法内部对引用类型内部的成员变量进行修改时,还是会影响到堆中的值。此时,useruserArg在内存中的存储情况如下:

    请添加图片描述

四、 笔/面试题目

  1. 问:Java是值传递还是引用传递

    答:Java是值传递

  2. 问:下面代码执行完之后,数组ints的元素值是多少

    public class Main {public static void main(String[] args) {int[] ints = new int[]{1, 2, 3};f(ints);}public static void f(int[] intsArg) {for (int i = 0; i < intsArg.length; i++) {intsArg[i] += 10;}}
    }
    

    答:数组也是一种引用类型,当调用方法f时,参入的是数组ints在栈中的值,该值指向堆中存存放数组具体内容的内存区域。当在方法f内部对数组内容进行操作时,将会修改堆中的内容,而由于intsintsArg指向同一块堆内存,因此ints所指向的数组也会被修改。因此最终数组ints的元素值是11、12、13

  3. 问:下面代码执行完之后,数组ints的元素值是多少

    public class Main {public static void main(String[] args) {int[] ints = new int[]{1, 2, 3};f(ints);}public static void f(int[] intsArg) {for (int i : intsArg) {i += 10;}}
    }
    

    答:这题与上题相识,但在方法f内部遍历数组的方式却不同:

    • 上一题是通过一个递增的变量i依次访问数组中的元素并修改元素的值
    • 本题是通过foreach语法依次取出数组中的元素值并将其赋值给一个新的变量i,然后修改这个变量i。修改的是变量i,并未对数组元素做修改

    因此最终数组ints的元素值仍然是1、2、3

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 深度学习-----------数值稳定性
  • docker部署jenkins和jenkins的基本使用
  • 【SpringCloud】SpringCloudNetflix笔记
  • 重塑购车体验,实时云渲染赋能东风日产探路云看车新体验
  • Date类型的字段序列化成JSON字符串
  • bug: 配置flyway.locations多个脚本位置不生效
  • SpringBoot整合支付宝沙箱支付流程(干货·精简版)Java毕业设计亮点 通俗易懂
  • Linux查看系统线程数
  • Python 爬虫项目实战五:抓取天猫超市商品信息
  • 数据结构 - 哈希表
  • 【C++进阶学习】第十二弹——C++ 异常处理:深入解析与实践应用
  • linux命令:scp与rsync
  • 关闭Windows安全中心
  • 个人编程原则总结(不喜勿喷)
  • 前缀和处理数组区间之和问题
  • CSS 三角实现
  • Django 博客开发教程 16 - 统计文章阅读量
  • javascript从右向左截取指定位数字符的3种方法
  • Java超时控制的实现
  • Java深入 - 深入理解Java集合
  • PHP面试之三:MySQL数据库
  • Python_OOP
  • React组件设计模式(一)
  • SpriteKit 技巧之添加背景图片
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 从PHP迁移至Golang - 基础篇
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 排序算法学习笔记
  • 区块链将重新定义世界
  • 我的业余项目总结
  • 详解NodeJs流之一
  • 优化 Vue 项目编译文件大小
  • 自定义函数
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • (09)Hive——CTE 公共表达式
  • (21)起落架/可伸缩相机支架
  • (3) cmake编译多个cpp文件
  • (7)STL算法之交换赋值
  • (9)目标检测_SSD的原理
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (NSDate) 时间 (time )比较
  • (Python) SOAP Web Service (HTTP POST)
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (web自动化测试+python)1
  • (蓝桥杯每日一题)love
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (十八)Flink CEP 详解
  • (十一)手动添加用户和文件的特殊权限
  • (一)基于IDEA的JAVA基础1
  • (转)jdk与jre的区别
  • (转)关于pipe()的详细解析
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .gitignore文件使用
  • .net项目IIS、VS 附加进程调试