目录

值传递or引用传递

相关概念

  1. 变量的访问 局部变量的访问是通过方法栈的基址+偏移量这种方式实现的。每个变量的偏移量在这个函数编译阶段就确定了。也就是说这个偏移量在具体生成的指令中是写死了的,所以变量名是不需要的。

对象的成员变量的访问是通过对象的基址+偏移量这种方式实现的。虽然这个偏移量也是在这个类的编译阶段就确定了,但是java类是动态加载的,也就是说其它类的方法中调用这个成员变量的时候是没法知道这个偏移量的,所以生成的指令里只能先暂时用成员变量名代替,待真正执行时根据加载类的信息将这个成员变量名替换成偏移量再访问,即解析、链接的过程。

  1. 字面量是指由字母,数字等构成的字符串或者数值,它只能作为右值出现,所谓右值是指等号右边的值,如:int a=123这里的a为左值,123为右值。 常量和变量都属于变量,只不过常量是赋过值后不能再改变的变量,而普通的变量可以再进行赋值操作。

  2. 每个线程有线程栈,每个方法在线程栈上有一格栈针,在栈上分配的好处,方法里的对象随方法结束而消失,栈针弹出去(栈顶指针往下压一格),方法里所有东西就没了。

值传递还是引用传递

字面量和对象引用是保存在栈上,对象是保存在堆上。栈上的引用指向堆上的对象。 传值的方式传引用。 或者说传值的方式传地址。你这个问题其实很简单。要搞清楚这个问题,要明白:堆 和 栈。引用是保存在栈上,对象是保存在堆中。引用指向堆上的对象,也就是说引用的值为对象在栈上的内存地址。那么你修改引用时改的是引用的值,也就是让引用指向其它对象。那么应该怎么修改堆上的对象呢?首先你得访问到堆上的对象吧?如何访问到它呢?在C/C++中是通过指针运算符 *p 来访问到指针p指向的堆上的对象的,然后再修改它。那么Java中没有指针运算符,那么怎么办呢?Java中是通过点操作符,也就是 list.add中的那个点,表示先访问到list这个引用指向的对象,然后让该对象调用 add 方法,从而修改了list指向的堆上的对象。所以当你单独修改 list = xxx;时你修改的是引用,让list引用指向其它对象,而没有修改 list 引用指向的对象,因为你根本就没有访问到堆上的对象,你怎么修改它呢?所以:要修改堆上的对象,你要先访问到它,不然你就不能修改它。访问堆上的对象的方法就是 . 点操作符。

举个例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class StringExer {
    String str = new String("good");
    char [] ch = {'t','e','s','t'};

    public void change(String str, char ch []) {
        str = "test ok";
        ch[0] = 'b';
    }

    public static void main(String[] args) {
        StringExer ex = new StringExer();
        ex.change(ex.str, ex.ch);
        System.out.println(ex.str);
        System.out.println(ex.ch);
    }
}

main调用change,首先在栈中push main栈针,再push change栈针, change栈针 局部变量表slot 1的位置存放str的引用,2的位置存放ch的引用

执行完方法体后slot 1的位置 值变成了指向字符串常量池中"test ok"的引用, 对str没有任何改变;

slot 2位置值没变, 修改了ch所指向的地址中保存的内容也就是ch[0]=‘b’。

所以实例变量str内容没变,ch变成{‘b’,‘e’,’s',’t'}

参考

java只有值传递,不存在引用传递 值传递与引用传递