小方法大门道

小瓜瓜作为一个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<A, B> {

    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<Boolean, Integer> returnTwoResult(Boolean flg, Integer num) {
flg = true;
num = 10;
return new TwoTuple<>(flg, num);
} public static void main(String[] args) {
TwoTuple<Boolean,Integer> 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<Boolean, Integer> 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<Boolean,Integer> result = returnTwoResult(false,5); System.out.println("first : " + result.first + ", second : " + result.second);
}
}

  

参考文章:

https://blog.csdn.net/javazejian/article/details/51192130

https://blog.csdn.net/fenglllle/article/details/81389286

https://www.hollischuang.com/archives/2700

https://www.zhihu.com/question/31203609

辨析Java方法参数中的值传递和引用传递的更多相关文章

  1. java方法参数传递方式只有----值传递!

    在通常的说法中,方法参数的传递分为两种,值传递和引用传递,值传递是指将实际参数复制一份传递到方法中, 在方法中的改动将不会影响到实际参数本身,而引用传递则是指传递的是实际参数本身,在方法中的改动将会影 ...

  2. JAVA方法中参数到底是值传递还是引用传递

    当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...

  3. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  4. Java中引用类型变量,对象,值类型,值传递,引用传递 区别与定义

    一.Java中什么叫做引用类型变量?引用:就是按内存地址查询       比如:String s = new String();这个其实是在栈内存里分配一块内存空间为s,在堆内存里new了一个Stri ...

  5. java中值传递和引用传递

    最近工作中使用到了值传递和引用传递,但是有点懵,现在看了下面的文章后清晰多了.一下是文章(网摘) 1:按值传递是什么 指的是在方法调用时,传递的参数是按值的拷贝传递.示例如下: public clas ...

  6. Java中的值传递和引用传递

    这几天一直再纠结这个问题,今天看了这篇文章有点思路了,这跟C++里函数参数为引用.指针还是有很大区别. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里 ...

  7. Java基础学习笔记十二 类、抽象类、接口作为方法参数和返回值以及常用API

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

  8. java中的值传递和引用传递有什么区别呀?

    值传递: (形式参数类型是基本数据类型和String):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参 ...

  9. Java中的值传递与引用传递

    1.基本类型和引用类型在内存中的保存 Java中数据类型分为两大类,基本类型和对象类型.相应的,变量也有两种类型:基本类型和引用类型. 基本类型的变量保存原始值,即它代表的值就是数值本身: 而引用类型 ...

随机推荐

  1. fulltext全文索引的使用

    Fulltext全文索引 Fulltext相关属性 查看数据库关于fulltext的配置 SHOW VARIABLES LIKE 'ft%'; -- ft就是FullText的简写 ft_boolea ...

  2. electron-vue多显示屏下将新窗口投放是其他屏幕

    display对象可以获取所有显示屏此处演示程序启动是投放新窗口至另一屏幕 import { app, BrowserWindow } from 'electron' const electron = ...

  3. suoermap的object.net循环遍历属性表,从数据库取数据进行更新属性字段值

    /// </summary> /// <param name="sName">图层名</param> /// <param name=&q ...

  4. 【Python】eval 函数

    eval() 函数十分强大 -- 将字符串 当成 有效的表达式 来求值,并返回计算结果 # 基本的熟悉计算 print(eval("1 + 1")) # 字符串重复 print(e ...

  5. ssm的maven项目启动tomcat时报错,Cannot find class: XXXX解决办法

    最近在写一个ssm的项目,启动总是报错.原因网上查了也没找到.最后终于解决.下面直接上代码 问题描述: 严重: Allocate exception for servlet ssm-dispatche ...

  6. 19,flask消息闪现-flash

    Flash消息 请求完成后给用户的提醒消息,flask的核心特性, flash函数实现效果 视图函数中调用flash()方法 html中要使用get_flashed_messages() 后端代码: ...

  7. 进程间通信之数据传输--FIFO

    One of the fundamental features that makes Linux and other Unices useful is the “pipe”. Pipes allow ...

  8. linux下给U盘分区&制作文件系统

    这几天读到TLCL-Storage Media一节,不由的想要折腾一下U盘,一直以来U盘只是被拿来暂存数据,其内部有没有文件系统,数据怎么管理,那是从来也不清楚,本文就依葫芦画瓢,折腾下手中的King ...

  9. python测试开发django-rest-framework-61.权限认证(permission)

    前言 用户登录后,才有操作当前用户的权限,不能操作其它人的用户,这就是需要用到权限认证,要不然你登录自己的用户,去操作别人用户的相关数据,就很危险了. authentication是身份认证,判断当前 ...

  10. Centos7-Gnome安装

    查看grouplist yum grouplist 安装gnome yum groupinstall "GNOME Desktop" root用户权限下,设置centos系统默认的 ...