深入理解java方法调用时的参数传递
深入理解java方法调用与参数传递,解决以下问题:Java方法调用是如何传递参数的?被调用方法对调用方法内的变量有什么影响?java能使用返回值void的中间方法对变量进行加工吗?为什么静态成员变量的改变影响是全局的?同一个方法同时被多个线程调用线程安全吗?1,每个线程都有一个方法链,即虚拟机栈。虚拟机栈是线程私有的,是方法执行的内存模型,每个栈帧都对应一个执行方法。开始执行,栈帧入栈,执行完毕,
·
深入理解java方法调用与参数传递,解决以下问题:
- Java方法调用是如何传递参数的?
- 被调用方法对调用方法内的变量有什么影响?
- java能使用返回值void的中间方法对变量进行加工吗?
- 为什么静态成员变量的改变影响是全局的?
- 同一个方法同时被多个线程调用线程安全吗?
1,每个线程都有一个方法链,即虚拟机栈。虚拟机栈是线程私有的,是方法执行的内存模型,每个栈帧都对应一个执行方法。开始执行,栈帧入栈,执行完毕,栈帧出栈。
2,局部变量表slot中的值:
基本类型为字面值,String等引用reference类型为指向堆内句柄的指针或直接指向堆内对象的指针,总之是地址值。(由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中的对象的具体位置,所以对象访问方式也是取决于虚拟机实现而定的。目前主流的访问方式有使用句柄和直接指针两种)
3,方法中变量的使用基于局部变量表的slot索引值:
每个方法执行都对应一个栈帧,都有独立的局部变量表,方法执行过程中
方法内部通过变量表的slot的索引值来定位和使用变量。所以一个方法调用另一个方法时,不可能将“某个栈的某个栈帧的某个索引值”这种定位描述传递过去,所以
方法调用时的参数传递采用值传递——基本类型的字面值、reference的地址值。
4,方法调用通过操作数栈实现参数传递:
概念模型下,栈帧上下独立,调用方法对应栈帧中局部变量表和操作数栈都均不受影响;虚拟机优化下,下面栈帧的操作数栈可以与上面栈帧的局部变量表重叠,无需额外的参数复制,调用方法对应栈帧中操作数栈内的值改变,局部变量表不受影响。
5,被调用方法对调用方法的影响:
首先,由上可知,每个栈帧的局部变量表都是独立的,不受方法调用影响。
值操作不会影响到调用方法变量,无论是基本类型的字面值还是引用类型的地址值;
a=10; //int a
a=“aaa”; //String a
a=new User(); //User a
实例操作改变了引用类型地址值指向的堆内实例信息,堆是共享的,所以调用方法内所看到的实例信息也是被改变的;
a.setName("aaa"); //User a
6,既然我们已经说过,每个栈帧的局部变量表都是独立的,参数传递又是值传递,为什么同一实例的方法执行会影响到成员变量,甚至是线程安全?reference类型可能是改变了实例信息,那么基本类型呢?
由于,存储在局部变量表0位Slot中的是指向自身实例的reference类型“this”。对成员变量的操作,即是对this实例信息的操作。同时单例多线程下,会造成线程安全。
线程安全解决方式:
- 原子操作(基本类型的读取和赋值):volatile关键字;
- 非原子操作:synchronized,Lock
- 非原子操作+指令重排序结果受影响:synchronized、 lock+volatile
- ThreadLocal<T>;
7,那么你说实例成员变量是因为0位Slot的引用类型“this”,那么静态成员变量呢?他为什么会在多例情况下,受到方法执行的影响,导致多例多线程的线程安全呢?
由于在类加载过程中的准备阶段,静态变量的内存被分配到方法区,赋初值(另外,初始化阶段,通过<Clinit>类构造器被初始化,且只执行一次)。而方法区和堆一样是线程共享的内存区域,即对所有线程的所有类型的所有实例的所有方法都是共享的,,,,,就是说谁都能使用它并更改它,且对整个虚拟机产生影响。
- 静态方法:静态方法中是没有this的引用,所以静态方法中不能直接使用实例变量只能使用静态变量(类变量),直接操作方法区中共享的变量内存;
- 实例方法:多例情况下,每个this指向一个实例,可以理解为通过操作每个this,间接操作着它们共享的静态变量(实际上,仍然是直接操作方法区中共享的变量内存);
注意:
- 静态变量在类加载中被立即解析成指向方法区内存的直接引用(指针),所以执行过程是直接操作方法区共享的变量内存。
- 实例变量被 立即解析 或 惰性解析 为相对偏移量,即相对对象头(实例所在内存地址的起始位置)的偏移值,以此来定位不同实例中的变量。
简单理解为:
- 方法调用的参数传递是值传递:基本类型字面值、引用类型地址值;
- 方法执行对应栈帧的局部变量表是独立的,不受方法调用影响;
- 值操作不会影响调用方法,但实例操作可以改变堆内实例信息,堆内存是共享的。
更多推荐
已为社区贡献3条内容
所有评论(0)