Java在对象类型上使用隐式指针语义,在原始数据类型上使用值语义。
值语义意味着您直接处理值并传递副本。重点在于,当您拥有一个值时,可以相信它不会在背后发生变化。
使用指针语义时,您没有一个值,而是一个“地址”。其他人可能会更改其中的内容,而您无法知道。
C ++中的指针语义:
void foo(Bar * b) ...
... b->bar() ...
您需要使用*来请求指针语义,并使用->在指针上调用方法。
Java中的隐式指针语义:
void foo(Bar b) ...
... b.bar() ...
由于您没有使用值语义的选择,因此不需要 *,也不需要 -> 和 . 之间的区别,因此采用了隐式方式。
基本上,值语义意味着将一个值分配给另一个值会创建一个副本:
int x = 1;
int y = x;
x = 2; // y remains the same!
特殊情况是函数调用传递了一个参数:
void f(int x) {
x = 5;
}
int a = 1;
f(a);
// a is still 1
对于Java和C++而言,它们实际上是相同的。然而,Java只知道一些原始类型,其中包括int
、double
、boolean
和char
,以及行为类似的枚举。所有其他类型都使用引用语义,这意味着一个值赋给另一个值时,实际上是重定向指针而不是复制底层的值:
class Foo {
int x;
public Foo(int x) { this.x = x; }
}
Foo a = new Foo(42);
Foo b = a; // b and a share the same instance!
a.x = 32;
//b.x is now also changed.
然而,需要注意的是有一些限制条件。例如,许多引用类型(如 String
,Integer
等)实际上是不可变的。它们的值无法更改,并且对它们的任何赋值都会覆盖旧值。
此外,参数仍然按值传递。这意味着传递给函数的对象的值可以更改,但其引用不能更改:
void f(Foo foo) {
foo.x = 42;
}
void g(Foo foo) {
foo = new Foo(42);
}
Foo a = new Foo(23);
f(a);
// a.x is now 42!
Foo b = new Foo(1);
g(b);
// b remains unchanged!
Java在变量访问时使用隐式指针语义
(您无法直接编辑引用,它会在访问时自动(隐式)解析为对象),并且在方法参数传递上也使用按值传递语义
。
在Java应用程序中,当一个对象引用是方法的参数时,您传递的是引用的副本(按值传递),而不是引用本身。请注意,调用方法的对象引用和副本都指向同一个对象。这是一个重要的区别。Java应用程序在传递各种类型的参数时不会像C++那样有所不同。Java应用程序通过值传递所有参数,因此无论类型如何,都会复制所有参数。
简而言之:Java中的所有参数都是按值传递的。但这并不意味着对象被复制了(就像PHP4中的默认设置一样),而是引用该对象的引用被复制了。
您将在Java应用程序中的传值语义中看到所有解释和深入示例。
Java 是按值传递的。 C++ 可以同时使用值语义和引用语义。
b
没有两个指针指向它,因为它本身就是一个指针(实际上是一个引用)。因此,b
和(在g
内部)foo
都指向/指向同一个对象。 - Konrad Rudolph