java值传递

笑笑虎Hello 发布于 2019/03/08 16:57
阅读 260
收藏 0

开源之夏第三届火热来袭,高校学生参与赢万元奖金!>>>

这段代码运行后的结果为什么是0啊,Java中不是传的是内存地址吗?形参和实参都指向total所在的内存空间,对total修改,应该打印的结果是1啊,求解

加载中
0
练打字的
练打字的

值传递,创建了副本

0
tcxu
tcxu
  1. 在Java API中, 定义了基本类型变量(数据)的包装类。比如 Integer是int包装类、Float是float的包装类、Char 是 char 包装类 等等。可以想象 String 就相当于是char[]的包装类。
  2. 对这些包装类的值操作实际上都是通过对其对应的基本类型操作而实现的。包装类的特质之一就是在对其值进行操作时会体现出其对应的基本类型的性质。
  3. 在参数传递时,包装类也是如此体现的。即 包装类的参数传递,体现为值传递。
  4. 换言之,"JAVA中的包装类型传参(Integer,String等对基本数据类型包装类),本质还是等同于值传递(即传递参数副本)。引用类型中传参除去JAVA基本类型的包装类。  String为例,Stringbuffer和StringBuild就符合引用传参特征!"

参考:

Java-String类型的参数传递问题
java 包装类型传参性质
类似包装类参数的值传递,字符串参数的值传递也可以见到。

import java.lang.Integer;
public class Test{
 public static void main(String args[]){
 String str = new String("笑笑虎Hello ");
 paramString(str);
 System.out.println(str);
 	}

 private static void paramString(String str){
 		str += "的提问。";  //在常量池内进行字符串的添加。
 	System.out.println( "paramString内 : " + str);
 }
}

输出:

paramString内 : 笑笑虎Hello 的提问。
笑笑虎Hello

 

1
1408
0
kakai
kakai

除了基本数据类型和其包装类是值传递,其它对象作为参数时都是引用传递。

0
清酒-暖风
清酒-暖风

基础和包装类型都是用内存的实际值替换

0
y
yuyangxp5

/**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;  /// <-------- 因为这个值是 final的啊!!

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

0
醉卧草庐听风雨

我想这类问题应该曾令每位Java开发者困惑,这里我给出我的答案 。下面是示例,为了方便解读,做了简化。

public class Question {
    public static void main(String[] args) {
        Integer i = 0;
        add(i);
    }

    public static void add(Integer num) {
        num += 1;
    }
}

Java是一个奇怪的语言,编写的源码要编译成Question.class字节码文件,再由Jvm执行,所以我们需要看看Question.class里究竟发生了什么,Jdk自带源码查看命令javap,使用javap -c Question.class可以得到如下主要的内容

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: aload_1
       6: invokestatic  #3                  // Method add:(Ljava/lang/Integer;)V
       9: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  args   [Ljava/lang/String;
            5       5     1     i   Ljava/lang/Integer;
 
  public static void add(java.lang.Integer);
    Code:
       0: aload_0
       1: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
       4: iconst_1
       5: iadd
       6: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       9: astore_0
      10: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0     i   Ljava/lang/Integer;

这里涉及字节码结构和jvm指令的知识,不复杂耐心分析分析。Code:jvm执行的代码,LocalVariableTable:方法的局部变量表。我们从main的开始看,0加载原始类型常量0,1调用Integer.valueOf方法包装为Integer,4存储Integer至局部变量表Slot为1的i中,自此完成对i的初始化。5加载局部变量i,6调用add方法。接着看看add中发生了什么,0加载add中局部变量表Slot为0的i,1调用方法Integer.intValue转为原始类型,4加载原始类型常量1,5数值相加,6调用Integer.valueOf方法包装为Integer,9存储Integer至i中,10完成add方法返回。所以在Jvm眼中我们的代码其实是这个样子

public class Question {
    public static void main(String[] args) {
        Integer i = Integer.valueOf(0);
        add(i);
    }

    public static void add(Integer i) {
        i = Integer.valueOf(i.intValue() + 1);
    }
}

这时main中变量i仍然指向堆中值为0的Integer的对象,而add中的i却已经只向值为1的Integer对象。这时有的人会说main中的i不是和add中的i是一样的吗?这就涉及到Java方法执行的参数的处理,一言以蔽之:Java方法调用是局部变量之间赋值转移,参数也只是个局部变量。可以看到main中的参数args同样是出现在局部变量表中Slot为0变量,在调add方法前的步骤5加载局部变量i,就是为add中变量i做赋值准备,那么例子就可以理解为如下内容

    public static void main(String[] args) {
        Integer main_i = Integer.valueOf(0);
        Integer add_i = main_i;
        add_i = Integer.valueOf(add_i.intValue() + 1);
    }

这样可以清晰的看出main中的i和add中的i的已是完全不同的Integer对象。建议阅读下Java虚拟机规范,可以大幅对Java认知和了解。

OSCHINA
登录后可查看更多优质内容
返回顶部
顶部
返回顶部
顶部