小方法大门道

小瓜瓜作为一个Java初学者,今天跟我说她想通过一个Java方法,将外部变量通过参数传递到方法中去,进行逻辑处理,方法执行完毕之后,再对修改过的变量进行判断处理,代码如下所示。

public class MethodParamsPassValue {

public static void doErrorHandle() {

boolean a = false;

int b = 5;

passBaseValue(a, b);

if (a == true || b == 10) {

System.out.println("Execute Something");

} else {

System.out.println("param result wrong");

}

}

public static void passBaseValue(boolean flg, int num) {

flg = true;

num = 10;

}

public static void main(String[] args) {

doErrorHandle();

}

}

上述代码是有问题的,布尔变量a和整型变量b在方法操作之后,它们的值并没有发生变化,小瓜瓜事与愿违。

究其原因

在Java方法中参数列表有两种类型的参数,基本类型和引用类型。

基本类型:值存放在局部变量表中,无论如何修改只会修改当前栈帧的值,方法执行结束对方法外不会做任何改变;此时需要改变外层的变量,必须返回主动赋值。

引用数据类型:指针存放在局部变量表中,调用方法的时候,副本引用压栈,赋值仅改变副本的引用。但是如果通过操作副本引用的值,修改了引用地址的对象,此时方法以外的引用此地址对象当然被修改。(两个引用,同一个地址,任何修改行为2个引用同时生效)。

这两种类型都是将外面的参数变量拷贝一份到局部变量中,基本类型为值拷贝,引用类型就是将引用地址拷贝一份。

方法参数为基本类型的值传递

public class MethodParamsPassValue {

public static void passBaseValue(boolean flg, int num) {

flg = true;

num = 10;

}

public static void main(String[] args) {

boolean a = false;

int b = 5;

System.out.println("a : " + a + " b : " + b);

passBaseValue(a, b);

System.out.println("a : " + a + " b : " + b);

}

}

返回结果

a : false b : 5

a : false b : 5

1. 方法参数flg被初始化为外部变量a的拷贝,值为false。参数num被初始化为外部变量b的拷贝,值为5。

2. 执行方法逻辑,方法中的局部变量flg被改变为true,局部变量flg被改变为10。

3.方法执行完毕,不再局部变量不再被使用到,等待被GC回收。

结论:当方法参数为基本类型时,是将外部变量值拷贝到局部变量中而进行逻辑处理的,故方法是不能修改原基本变量的。

方法参数为包装类型的引用传递

public class MethodParamsPassValue {

public static void passReferenceValue(Boolean flg, Integer num) {

flg = true;

num = 10;

}

public static void main(String[] args) {

Boolean a = false;

Integer b = 5;

System.out.println("a : " + a + " b : " + b);

passReferenceValue(a, b);

System.out.println("a : " + a + " b : " + b);

}

}

结果为

a : false b : 5

a : false b : 5

当传入参数为包装类型时,为对象的引用地址拷贝。那么既然是引用拷贝为什么还是没有更改原来的包装类型的变量值呢?

这是因为Java中的自动装箱机制,当在方法中执行 flg = true 时,实际在编译后执行的是 flg = Boolean.valueOf(true),即又会产生一个新的Boolean对象。同理Integer num也是如此。

方法参数为类的对象引用时

public class ParamObject {

private boolean flg;

private int num;

public ParamObject(boolean flg, int num) {

this.flg = flg;

this.num = num;

}

public boolean isFlg() {

return flg;

}

public void setFlg(boolean flg) {

this.flg = flg;

}

public int getNum() {

return num;

}

public void setNum(int num) {

this.num = num;

}

@Override

public String toString() {

return "ParamObject{" +

"flg=" + flg +

", num=" + num +

'}';

}

}

public class MethodParamsPassValue {

public static void passObjectValue(ParamObject paramObject) {

paramObject.setFlg(true);

paramObject.setNum(10);

}

public static void main(String[] args) {

ParamObject a = new ParamObject(false, 5);

System.out.println(a);

passObjectValue(a);

System.out.println(a);

}

}

结果为

ParamObject{flg=false, num=5}

ParamObject{flg=true, num=10}

结论:对于引用类型的方法参数,会将外部变量的引用地址,复制一份到方法的局部变量中,两个地址指向同一个对象。所以如果通过操作副本引用的值,修改了引用地址的对象,此时方法以外的引用此地址对象也会被修改。(两个引用,同一个地址,任何修改行为2个引用同时生效)。

脑筋急转弯之'交换两个对象'

public class MethodParamsPassValue {

public static void swapObjectReference(ParamObject object1, ParamObject object2) {

ParamObject temp = object1;

object1 = object2;

object2 = temp;

}

public static void main(String[] args) {

ParamObject a = new ParamObject(true, 1);

ParamObject b = new ParamObject(false, 2);

System.out.println("a : " + a + " b : " + b);

swapObjectReference(a, b);

System.out.println("a : " + a + " b : " + b);

}

}

结果为

a : ParamObject{flg=true, num=1} b : ParamObject{flg=false, num=2}

a : ParamObject{flg=true, num=1} b : ParamObject{flg=false, num=2}

有了上面的知识之后,我们会发现这个方法中的引用地址交换,只不过是一个把戏而已,只是对方法中的两个局部变量的对象引用值进行了交换,不会对原变量引用产生任何影响的。

一个方法返回两个返回值

Java方法中只能Return一个返回值,那么如何在一个方法中返回两个或者多个返回值呢?我们可以通过使用泛型来定义一个二元组来达到我们的目的。

public class TwoTuple {

public final A first;

public final B second;

public TwoTuple(A a, B b) {

first = a;

second = b;

}

public String toString() {

return "(" + first + ", " + second + ")";

}

}

public class MethodParamsPassValue {

public static TwoTuple returnTwoResult(Boolean flg, Integer num) {

flg = true;

num = 10;

return new TwoTuple<>(flg, num);

}

public static void main(String[] args) {

TwoTuple result = returnTwoResult(false,5);

System.out.println("first : " + result.first + ", second : " + result.second);

}

}

完整代码

package com.lingyejun.authenticator;

public class MethodParamsPassValue {

public static void doErrorHandle() {

boolean a = false;

int b = 5;

passBaseValue(a, b);

if (a == true || b == 10) {

System.out.println("Execute Something");

} else {

System.out.println("param result wrong");

}

}

/**

* 基本类型,赋值运算=,会直接改变变量的值,原来的值被覆盖掉

* 引用类型,复制运算=,会改变引用中所保存的地址,旧地址被覆盖掉,但原来的对象不会改变。

*

* @param flg

* @param num

*/

public static void passBaseValue(boolean flg, int num) {

flg = true;

num = 10;

}

public static void passReferenceValue(Boolean flg, Integer num) {

flg = true;

num = 10;

}

public static void passObjectValue(ParamObject paramObject) {

paramObject.setFlg(true);

paramObject.setNum(10);

}

public static void swapObjectReference(ParamObject object1, ParamObject object2) {

ParamObject temp = object1;

object1 = object2;

object2 = temp;

}

public static TwoTuple returnTwoResult(Boolean flg, Integer num) {

flg = true;

num = 10;

return new TwoTuple<>(flg, num);

}

public static void main(String[] args) {

doErrorHandle();

System.out.println("============================");

boolean initFlg = false;

int initNum = 5;

System.out.println("init flg : " + initFlg + " init num : " + initNum);

passBaseValue(initFlg, initNum);

System.out.println("init flg : " + initFlg + " init num : " + initNum);

System.out.println("============================");

Boolean referenceFlg = false;

Integer referenceNum = 5;

System.out.println("reference flg : " + referenceFlg + " reference num : " + referenceNum);

passReferenceValue(referenceFlg, referenceNum);

System.out.println("reference flg : " + referenceFlg + " reference num : " + referenceNum);

System.out.println("============================");

ParamObject paramObject = new ParamObject(false, 5);

System.out.println(paramObject);

passObjectValue(paramObject);

System.out.println(paramObject);

System.out.println("============================");

ParamObject object1 = new ParamObject(true, 1);

ParamObject object2 = new ParamObject(false, 2);

System.out.println("object1 : " + object1 + " object2 : " + object2);

swapObjectReference(object1, object2);

System.out.println("object1 : " + object1 + " object2 : " + object2);

System.out.println("============================");

TwoTuple result = returnTwoResult(false,5);

System.out.println("first : " + result.first + ", second : " + result.second);

}

}

参考文章:

java值传递和引用传递_辨析Java方法参数中的值传递和引用传递相关推荐

  1. swing查询输入框无值时出现null异常_如何优雅处理代码中 Null 值引起的 Bug?告别 Null 恐惧症!...

    导语 在笔者几年的开发经验中,经常看到项目中存在到处空值判断的情况,这些判断,会让人觉得摸不这头绪,它的出现很有可能和当前的业务逻辑并没有关系.但它会让你很头疼. 有时候,更可怕的是系统因为这些空值的 ...

  2. java gc回收堆还是栈_浅析JAVA的垃圾回收机制(GC)

    1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制. 注意:垃圾回 ...

  3. 获取键盘录入的5个int数,并存放到int数组arr中,输入前提示输入的是第几个值 * * 2.2 传递数组arr调用getNum(int[] arr)方法,获取返回值,并打印输出

    package Day05;import java.util.Scanner;/*** 2.定义main方法:* * 2.1 获取键盘录入的5个int数,并存放到int数组arr中,输入前提示输入的是 ...

  4. 重学Java(035)——Java基础知识(类作为成员变量、接口作为成员变量、接口作为方法参数和返回值类型)

    学习内容:三种引用类型用法 一.类作为成员变量 二.接口作为成员变量 三.接口作为方法参数和返回值类型 实际的开发中,引用类型的使用非常重要,也是非常普遍的.我们可以在理解基本类型的使用方式基础上,进 ...

  5. NO.A.0011——day05——数组的-索引/内存/遍历/最大值获取/反转/作为方法参数和返回值...

    一.数组的概念 1.1.数组的概念:动态初始化: 代码库:Demo01Array.java package cn.itcast.day05.demo01;/* 数组的概念:是一种容器,可以同时存放多个 ...

  6. aop对请求后端的参数修改_Spring Aop 修改目标方法参数和返回值

    @Component("changeIdNoAopHandler")public classChangeIdNoAopHandler {private static Logger ...

  7. 测试私有方法_Java基础之抽象类、接口作为方法参数和返回值

    不同修饰符使用细节 常用来修饰类.方法.变量的修饰符 public 权限修饰符,公共访问, 类,方法,成员变量 protected 权限修饰符,受保护访问, 方法,成员变量 默认什么也不写 也是一种权 ...

  8. numpy中方法参数axis取值理解

    numpy中方法参数axis取值理解 首先不要使用什么横纵轴去理解,因为时间长了就忘记了,而且如果a是一个三维数组就没法解释了. axis等于0时,在shape中表示的是二维数组.那么np.amin( ...

  9. JAVA用Math 给pi赋值_导入Math.PI作为参考或值

    我正在准备使用Java进行基本认证. 我对正确的问题的答案感到困惑(!):- 鉴于: public class Circle { static double getCircumference(doub ...

最新文章

  1. 开启Linux下Telnet服务
  2. webpack+vue+mint-ui 实现上拉加载更多(Loadmore组件)
  3. Dreamweaver 2020安装教程
  4. 什么是光纤转换器?光纤转换器转换类别介绍
  5. 【POJ - 1836】Alignment(dp,LIS,最长上升子序列类问题)
  6. uboot的目录分析
  7. caffe 图片数据的转换成lmdb和数据集均值(转)
  8. NVIDIA 修复 GPU 驱动中的多个代码执行缺陷
  9. FFmpeg实战命令(不断更新中...)
  10. pcie扰码的作用_扰码讲解
  11. chromedriver与chrome各版本及下载地址
  12. C语言笔试题2022
  13. 阿里云ECS迁移至腾讯云
  14. Math.h 正态分布 C语言,C++与正态分布(示例代码)
  15. 微信 进入公众号获取地理位置
  16. ES的聚合操作(API版本)
  17. 表白套路计算机公式,高级表白密码我喜欢你公式 套路喜欢的人
  18. 解释相机中的弥散现象
  19. WPF:鼠标拖曳、拖动控件
  20. 如何插入Word文件内容到PDF,或者混合重新排列多个PDF页面

热门文章

  1. 本地跨域处理ajax,Node.js配合node-http-proxy解决本地开发ajax跨域问题
  2. 3-8Tensor的算术运算编程实例
  3. centos5.9 安装mysql_centos 5.9 安装mysql 5.5.31
  4. html 点击选择变色,JS实现菜单点击后变色
  5. C# set和get如何用
  6. python怎么分析数据结构_《利用Python进行数据分析》第五章-pandas的数据结构介绍...
  7. emmc linux 识别分区_EMMC芯片电视主板直写厂家引导程序
  8. java yml value_Spring Boot:从YAML文件加载@Value
  9. Java 并发编程之同步工具类栅栏 CyclicBarrier
  10. 起泡排序验证性实验(2)