Java引用与C指针有何不同?


97

C有指针,Java有所谓的引用。从某种意义上说,它们有共同点,即它们都指向某个事物。我知道C中的指针存储它们指向的地址。参考也存储地址吗?除了指针更灵活,更容易出错之外,它们又有何不同?


10
注意C ++还具有与指针或java引用不同的引用
jk。

@jk。我以为和Java一样,有什么区别?
Gnijuohz 2012年

17
C ++引用不可重新绑定(即,您不能更改指定的对象),并且不能为空(即,您不能有效地使它们完全不引用任何对象)。
AProgrammer 2012年

@Gnijuohz改写@AProgrammer所说的话:finalJava中的引用几乎等同于C ++引用。它们不完全相等的原因是,C ++引用另外不可为空,而finalJava中的引用则可为空。
Utku

Answers:


142

引用可以通过存储地址来实现。 通常, Java引用将被实现为指针,但这不是规范所必需的。他们可能正在使用附加的间接层,以使垃圾收集更加容易。但是最后,它将(几乎总是)归结为涉及(Java风格)引用实现的(C风格)指针。

您不能使用引用进行指针运算。C语言中的指针与Java语言中的引用之间最重要的区别是,您实际上无法获取(和操纵)Java语言中的引用的基础值。换句话说:您不能执行指针运算。

在C语言中,您可以向指针(即地址)添加某些内容,也可以减去某些内容以指向“附近”的内容或指向任意位置的内容。

在Java中,引用仅指向一件事。您可以使变量持有不同的引用,但您不能仅仅要求它指向“原始事物之后的事物”。

引用是强类型的。另一个区别是,与C中的指针类型相比,Java 中对引用类型的控制严格得多。在C中,您可以具有an int*并将其强制转换为a,char*然后重新解释该位置的内存。这种重新解释在Java中不起作用:您只能将引用另一端的对象解释为已经存在的某种东西(即Object仅当指向的对象实际上是a时,才能将引用转换为String引用)。String

这些差异使C指针更强大,但也更加危险。这两种可能性(指针算术和重新解释所指向的值)都为C增加了灵活性,并且是该语言某些功能的源泉。但是它们也是问题的主要根源,因为如果使用不当,它们很容易破坏您的代码构建的假设。而且很容易错误地使用它们。


18
+1 力量。不要依赖实施细节。
CVn 2012年

2
+1垃圾收集是否值得一提?这是C指针功能更强大但更危险的另一种方式(悬空指针指向释放的内存有导致内存损坏的风险,存在内存泄漏的风险)
MarkJ 2013年

引用和指针之间的另一个区别是C中的指针可以转换为一系列数字(例如,通过使用memcpy将一个移动到char[]),反之亦然。如果将指针转换为存储在某个位置的数字序列(可能在屏幕上显示并由操作员在纸条上向下复制),则计算机内的指针的所有副本都会被破坏,并且该数字序列将被转换回到一个指针(也许在操作员输入之后),该指针仍必须指向它之前所做的相同的事情。一个程序...
超级猫

...将指针显示为数字,然后将手动输入的数字转换为指针,可能是“邪恶的”,但是除非操作员键入了未显示以形成有效数字的数字,否则它不会调用任何未定义行为指针。因此,在完全可移植的C语言中不可能实现通用垃圾收集,因为计算机无法知道宇宙中某处是否存在指针的副本。
2014年

根据JLS,第4.3.1节,Java中的引用是指针,因此“通常Java引用将实现为指针,但这不是规范所必需的。” 是错误的。
卢布洛赫(Lew Bloch)

8

C ++引用再次不同。

它们必须被初始化,并且不能为null(至少在格式正确的程序中不能为空),并且不能重新定位以引用其他内容。C ++引用更像是对象的别名。

指针和Java / C ++引用之间的另一个重要区别是,您可以获取指针的地址,而不能访问引用的地址(实际上,C ++引用实际上根本不需要作为对象存在于内存中),因此您可以拥有一个指向指针但不指向引用的指针


4

Java参考和C指针在两点上完全不同:

  1. 前者没有指针算法。
  2. 而且,您无法创建对所需内容的Java引用,只能复制那些保存在可访问的位置(静态字段,对象字段,局部变量)或由函数调用(如构造函数调用)返回的内容,因此它们均引用Java对象(永不基本类型,如引用charint等等)。

有人写道,引用是强类型的,因为您不能强制编译器将an int*视为a char*
完全排除特定转换实际上是安全的事实,C语言中没有多态性,因此比较是不容易的。
当然,Java比C具有更强的类型,并不是那是C指针与Java引用的功能,您需要使用JNI来打破类型安全性(除了忽略通用限制),但是即使在C中,您也必须强制编译器。

有人写道,Java引用可以实现为C指针,我敢肯定,由于JVM是用C实现的,因此它们在功能上通常不那么强大,因为它们在功能上通常不那么强大。尽管在64位计算机上,它们通常是压缩的普通对象指针(“压缩的OOP”),以节省空间和带宽。
无论如何,这些C指针也不必等同于硬件地址,即使它们通常(> 99%的实现)是出于性能原因。
最后,这是未向程序员公开的实现细节。


-1

它们略有不同。在Java中,引用的副本被复制到被调用函数的堆栈中,指向与调用函数相同的对象,并允许您操纵该对象。但是,您不能更改调用函数所引用的对象。

考虑下面的java代码

public static void changeRValue(StringBuffer sb){
    sb = new StringBuffer("helllllo"); /*attempt to assign the reference
                                        to a new object*/
}
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("hi");     //Create a new string buffer
    changeRValue(sb);                             //Call changeRValue
    System.out.println(sb.toString());            //Prints "hi" not "hello"
}

现在考虑一下c ++中的指针:

void func(Dog* dog){
    *dog = Dog("hello world"); //Change the value of dog to a new object
}

int main(int argc, const char * argv[]) {
    Dog dog1("hi");                            //Create a dog object
    func(&dog1);                               //pass the address of dog
    cout << dog1.name;                         //Prints "hello world" not hi.
    return 0;
}

我想我可能会补充说,它可以被视为类似于const Dog *
Eladian

2
您可以像使用C ++一样轻松地在Java中修改指向对象。您只需要访问合适的成员即可。Java对象没有赋值运算符,因为Java不支持用户定义的运算符重载,因此您不能使用重载的运算符。Java的缺乏与Java引用与使用指针算术的C ++指针相同的事实无关。
Deduplicator 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.