我们先来看几个例子. 先来Java的吧. Java最假了. 一般人都认为是引用传递(基本类型值传递). 所以我们又拿C++做例子.
写 a , b 是为了好区分开. 其实 写俩a都行. 不好书面表达
public class Demo {
public static void main(String[] args) {
A a = new A();
System.out.println("初始化 : " + a.name);
a.name = "main";
System.out.println("init函数调用前 : " + a.name);
// change 函数传递
init(a);
// 结果 ?
System.out.println("init函数调用后 : "+a.name);
}
static void init(A b) {
// a被初始化了..
b = new A();
}
private static class A {
String name;
}
}
输出 :
初始化 : null
init函数调用前 : main
init函数调用后 : main
结果是啥. 如果真的是引用传递. 那么为啥a.name为啥不是空呢.
我用C++ 给大家写一下.
```c++
using namespace std;
class A { public: string name; };
void init(A *b) { cout << "未改变的地址 : " << b << endl; *b = A(); cout << "改变后的地址 : " << b << endl; } int main(int argc, char const *argv[]) { A *a = new A(); cout << "初始化 : " << a->name << endl; a->name = "main"; cout << "init函数调用前 : " << a->name << endl; init(a); cout << "init函数调用后 : " << a->name << endl; return 0; } ```
输出
初始化 :
init函数调用前 : main
未改变的地址 : 0xe699d0
改变后的地址 : 0xe699d0
init函数调用后 :
显然C++和Java的表现是不一样的. 为什么C++ 赋值可以改变值. 而Java却不是呢.
我们先解释C++的做法.
我们init(A *b)做了啥
首先是我们定义了一个变量 A *a=new A("main") , 我没有写构造函数, 假设的.
此时传递给函数init(A *b) , 此时 b=a ,a= 0xe699d0 . 所以呢 b= 0xe699d0 .
后面我们的操作就是基于这个 b 的. 此时我们将*b =A(); , 是不是将0xe699d0指针指向了 A() 对象呢(其实就是块内存,指向的是内存的首地址) . 此时是不是修改了0xe699d0指针 的指向呢. 那么a也等于 0xe699d0 .所以a的值也被修改了 .
然后我们看看Java的做法.
static void init(A b) {
System.out.println("未改变的地址: 0x"+Integer.toHexString(a.hashCode()));
// a被初始化了..
b = new A();
System.out.println("改变后的地址: 0x"+Integer.toHexString(a.hashCode()));
}
我们打印一下 hashcode. 因为Java的hashcode其实就是确定一个对象的唯一标识, 如果你想深入了解hashcode的话介意看看JVM的源码,C++和Java对象的映射关系 (如果是对象地址的话,那么内存回收会改变大量的内存地址,难道还是地址吗,我们这里不考虑这个.只要知道这个是Java对象的唯一值类似于hash码 ).
输出结果:
初始化 : null
init函数调用前 : main
未改变的地址: 0x6e8cf4c6
改变后的地址: 0x12edcd21
init函数调用后 : main
此时我们发现地址发生了改变. 其实理解了上面C++那部分聪明的就明白了.
由于Java引用类型传递如果是Hotspot虚拟机则实现的就是简单的指针传递. 这个变量指向Java对象的数据区域.
由于一开始 a=0x6e8cf4c6(Java引用对象) , 然后函数赋值 b=a , 此时 b= 0x6e8cf4c6.
关键点在于new A()地方; 执行的是,实例化一个A对象,在栈顶开辟一块空间, 将A对象保存在栈顶中, 此时栈顶值=0x12edcd21, 然后将栈顶值存入到变量b中. 此时b=0x12edcd21 . 那么改变原来0x6e8cf4c6指向的内容了吗,并没有.
我们再看看javac编译后的结果 :
static void init(com.jvm.reference.Demo$A);
descriptor: (Lcom/jvm/reference/Demo$A;)V
flags: ACC_STATIC
Code:
// 栈的深度需要3 , 变量表需要一个,参数一个
stack=3, locals=1, args_size=1
// 实例化一个对象
0: new #2 // class com/jvm/reference/Demo$A
3: dup
4: aconst_null
// 调用构造方法
5: invokespecial #3 // Method com/jvm/reference/Demo$A."":(Lcom/jvm/reference/Demo$1;)V
// 将栈顶值存入变量0中
8: astore_0
// 返回
9: return
准确点说Java的做法是 : 如下操作.
void init(A *b)
{
cout << "未改变的地址 : " << b << endl;
A a = A();
b = &a;
cout << "改变后的地址 : " << b << endl;
}
其实各种做法也有各种做法的好处. 没有绝对的好坏.
其实你理解上面这个例子. 你就明白了为啥值传递了. 我们继续拿C++说话. Java查看地址不方便.
#include
using namespace std;
void swap(int *a, int *b)
{
int *temp = a;
a = b;
b = temp;
cout << "a : " << *a << " , b : " << *b << endl;
}
int main(int argc, char const *argv[])
{
int x = 1;
int y = 2;
cout << "x : " << x << " , y : " << y << endl;
// swap 方法.
swap(&x, &y);
cout << "x : " << x << " , y : " << y << endl;
/* code */
return 0;
}
输出
x : 1 , y : 2
a : 2 , b : 1
x : 1 , y : 2
我们发现为啥函数内部 . a 和 b 成功交换了地址 . 所以 a 和 b的值就互换了 .
但是为啥呢.
我们再次打印一下地址
cout << "&x : " << &x << " , &y : " << &y << endl;
// 输出 :
&x : 0x61fefc , &y : 0x61fef8
&x = 0x61fefc , &y=0x61fef8 ,
执行swap函数. 此时 a=0x61fefc , b=0x61fef8 ,
然后经过一番操作, 此时 a=0x61fef8 , b=0x61fefc . 然后输出 *a=2, *b=1, 所以成功了.
但是为啥 x 和 y 没变呢. 是不是发现 &x 和 &y依旧着原来的地址呢.
正确的swap操作 , 必须修改指针指向的值.
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
创建对象自然是为了后续使用该对象,我们的Java程序会通过栈上的reference数据来操作堆上的具体对象。由于reference类型在《Java虚拟机规范》里面只规定了它是一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置,所以对象访问方式也是由虚拟机实现而定的,主流的访问方式主要有使用句柄和直接指针两种:
本文第三节引用自 <<深入理解Java虚拟机>> .
这两种对象访问方式各有优势 :
使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。
使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本,就本书讨论的主要虚拟机HotSpot而言,它主要使用第二种方式进行对象访问(有例外情况,如果使用了Shenandoah收集器的话也会有一次额外的转发,具体可参见第3章),但从整个软件开发的范围来看,在各种语言、框架中使用句柄来访问的情况也十分常见。
所以句柄访问方便管理, 直接指针访问效率高, 但是不方便管理.
页面更新:2024-02-19
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号