我如何在Java中使用指针?

149

我知道Java没有指针,但我听说Java程序可以使用指针创建,并且只有少数精通Java的人才能做到。这是真的吗?


2
无论如何,在Sun的实现中。 - Michael Myers
1
我可以使用那个指针地址吗? - user213038
7
Java对象的默认hashCode不是指针地址。仔细阅读hashCode的规定,你会发现内存中的两个不同对象可能会拥有相同的hashCode值。 - Amir Afghani
3
在64位和32位Java中,32位的哈希码不是地址。哈希码并不保证唯一性。当对象在空间之间移动并且内存被压缩时,对象的位置可能会改变,但是哈希码不会改变。 - Peter Lawrey
2
你可以用Java引用实现C++指针的90%功能,剩下的10%可以通过将引用封装在另一个对象中来实现(虽然我从未发现有必要这样做)。 - Richard Tingle
显示剩余3条评论
17个回答

282

Java中的所有对象都是引用,您可以像使用指针一样使用它们。

abstract class Animal
{...
}

class Lion extends Animal
{...
}

class Tiger extends Animal
{   
public Tiger() {...}
public void growl(){...}
}

Tiger first = null;
Tiger second = new Tiger();
Tiger third;

空指针解引用:

first.growl();  // ERROR, first is null.    
third.growl(); // ERROR, third has not been initialized.

别名问题:

third = new Tiger();
first = third;

失去单元格:

second = third; // Possible ERROR. The old value of second is lost.    

您可以通过先确保不再需要 second 的旧值或将另一个指针赋值为 second 的值来使其更安全。

first = second;
second = third; //OK
请注意,以其他方式(NULL、new...)为second参数赋值同样可能导致错误,并可能导致指针指向的对象丢失。
当您调用new并且分配器无法分配请求的单元时,Java系统将抛出异常(OutOfMemoryError)。这很少见,通常是由于运行逃逸递归引起的。
请注意,从语言角度来看,将对象交给垃圾收集器不是错误。这只是程序员需要注意的事情。同一变量可以在不同时间指向不同的对象,并且当没有指针引用它们时,旧值会被回收。但是,如果程序的逻辑要求至少保留一个对对象的引用,则会导致错误。
初学者经常犯以下错误。
Tiger tony = new Tiger();
tony = third; // Error, the new object allocated above is reclaimed. 
您可能想表达的是:
Tiger tony = null;
tony = third; // OK.

类型转换错误:

Lion leo = new Lion();
Tiger tony = (Tiger)leo; // Always illegal and caught by compiler. 

Animal whatever = new Lion(); // Legal.
Tiger tony = (Tiger)whatever; // Illegal, just as in previous example.
Lion leo = (Lion)whatever; // Legal, object whatever really is a Lion.

C语言中的指针:

void main() {   
    int*    x;  // Allocate the pointers x and y
    int*    y;  // (but not the pointees)

    x = malloc(sizeof(int));    // Allocate an int pointee,
                                // and set x to point to it

    *x = 42;    // Dereference x to store 42 in its pointee

    *y = 13;    // CRASH -- y does not have a pointee yet

    y = x;      // Pointer assignment sets y to point to x's pointee

    *y = 13;    // Dereference y to store 13 in its (shared) pointee
}

Java中的指针:

class IntObj {
    public int value;
}

public class Binky() {
    public static void main(String[] args) {
        IntObj  x;  // Allocate the pointers x and y
        IntObj  y;  // (but not the IntObj pointees)

        x = new IntObj();   // Allocate an IntObj pointee
                            // and set x to point to it

        x.value = 42;   // Dereference x to store 42 in its pointee

        y.value = 13;   // CRASH -- y does not have a pointee yet

        y = x;  // Pointer assignment sets y to point to x's pointee

        y.value = 13;   // Deference y to store 13 in its (shared) pointee
    }
} 

更新:如评论中建议的那样,必须注意C具有指针算术运算。但是,在Java中我们没有这个。


23
很好的回答,但你忽略了一个关键区别:C有指针算术运算(Pointer Arithmetics),而Java没有。(幸运的是)在Java中无法进行这种操作。 - R. Martinho Fernandes
24
我不喜欢给任何答案打差评,尤其是一个受欢迎的答案,但是Java没有指针,你不能像使用指针一样使用引用。有些事情只能用真正的指针来完成 - 例如,您可以获取或设置进程数据空间中的任何字节。在Java中,您根本无法做到这一点。在我看来,很多人似乎不理解指针的真正含义,这真的很糟糕。现在我要下线了,希望你能原谅我小小的情绪爆发。 - Danger
8
我认为你的意思是"不幸地"。你为什么认为Java不支持指针算术是件好事? - user3462295
3
因为人们似乎认为普通大众难以使用它,只有编写编译器或设备驱动程序的编码忍者才能理解它。 - iCodeSometime
7
Java中的所有对象都是引用,你可以像指针一样使用它们。这是最好的答案。如果唯一的答案是“不,Java没有指针”,那么这个回答将是无益的。相反,这个回答说明了你可以采取什么替代方案。 - csga5000
显示剩余6条评论

66

由于Java没有指针数据类型,因此在Java中无法使用指针。即使是少数专家也无法在Java中使用指针。

另请参见:Java语言环境中的最后一点。


3
这里很少有正确答案,其中一个正确答案的赞数却很少? - Danger
1
请注意,存在模拟指针行为的“指针”对象。对于许多目的来说,这可以以相同的方式使用,尽管不涉及内存级别寻址。考虑到问题的性质,我觉得奇怪的是没有其他人提到这一点。 - kingfrito_5005
这应该是最好的答案。因为对于初学者来说,“在幕后Java有指针”可能会导致非常混乱的状态。即使在Java的起始级别上进行教学,也要教授指针不安全,这就是为什么它们不在Java中使用的原因。程序员需要被告知他/她可以看到和处理什么。引用和指针实际上是非常不同的。 - Neeraj Yadav
1
@Sometowngeek,链接的文档是1996年描述Java第一个版本的白皮书。枚举类型是在Java 1.5版本(2004年)中引入的。 - Fortega
Java有指针。在Java中,Dog d = new Dog(); d.bark();与C++中的Dog *d = new Dog(); d->bark();是相同的。不同之处在于,在Java中,您无法获取对非对象的指针的引用,并且无法解除引用对象指针。您也无法将其转换为数字并进行数学运算。通过null指针访问对象将获得NullPointerException - Michael Tsang
显示剩余2条评论

51

Java确实有指针。在Java中,每当你创建一个对象时,实际上是在创建一个指向该对象的指针;这个指针可以被设置为不同的对象或null,而原始对象仍将存在(等待垃圾回收)。

但在Java中不能进行指针算术运算。你无法解引用特定的内存地址或递增一个指针。

如果你真的想要获得低级别的操作,唯一的方法是使用Java Native Interface;即使这样,低级别的部分也必须用C或C++来完成。


17
Java根本没有指针,简单明了。我不明白为什么这里有那么多错误的回答。这可是StackOverflow啊!在计算机科学中,指针的定义是:“指针是一种编程语言对象,其值引用(或“指向”)存储在计算机内存中其他地方的另一个值,使用其地址进行访问。”在Java中,引用不是指针。Java需要运行垃圾收集,如果你有一个真正的指针,那么它就会出错! - Danger
5
指针是包含内存地址的数据。Java有两个方面,人们经常会忘记这一点。它既是一种语言,又是一个平台。就像我们不会说.NET是一种语言一样,我们需要记住说“Java没有指针”可能会引起误解,因为平台当然具有和使用指针。这些指针不能通过Java语言访问,但它们确实存在于运行时中。语言中的引用存在于运行时中实际的指针之上。 - kingfrito_5005
@kingfrito_5005 看起来你同意我的观点。我相信你说过“那些指针在Java语言中对程序员不可访问”,这与我的说法“Java没有指针”是一致的。引用和指针在语义上有显著的区别。 - Danger
2
@危险,这是真的,但我认为在这些情况下区分平台和语言很重要,因为我知道当我刚开始学习时,像你这样的陈述会让我感到困惑。 - kingfrito_5005
1
是的,Java 变量后面肯定有指针。只是开发人员不必处理它们,因为 Java 在幕后进行了魔术。这意味着开发人员不能直接将变量指向特定的内存地址,并处理数据偏移等问题。它允许 Java 保持干净的内存垃圾回收,但由于每个变量本质上都是一个指针,因此还需要一些额外的内存来存储所有这些自动生成的指针地址,而在更低级别的语言中可能可以避免这种情况。 - VulstaR

12

Java 中存在指针,但是你不能像在 C++ 或 C 中那样操纵它们。当你传递一个对象时,你正在传递一个指向该对象的指针,但不是像在 C++ 中那样。该对象无法被引用。如果你使用其本机访问器设置其值,它将发生变化,因为 Java 通过指针知道了其内存位置。但是指针是不可变的。当你试图将指针设置为一个新的位置时,你最终得到的是一个具有相同名称的新本地对象。原始对象没有改变。下面是一个简要的程序来演示这种差异。

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone {

    public static void main(String[] args) throws java.lang.Exception {
        System.out.println("Expected # = 0 1 2 2 1");
        Cat c = new Cat();
        c.setClaws(0);
        System.out.println("Initial value is " + c.getClaws());
        // prints 0 obviously
        clawsAreOne(c);
        System.out.println("Accessor changes value to " + c.getClaws());
        // prints 1 because the value 'referenced' by the 'pointer' is changed using an accessor.
        makeNewCat(c);
        System.out.println("Final value is " + c.getClaws());
        // prints 1 because the pointer is not changed to 'kitten'; that would be a reference pass.
    }

    public static void clawsAreOne(Cat kitty) {
        kitty.setClaws(1);
    }

    public static void makeNewCat(Cat kitty) {
        Cat kitten = new Cat();
        kitten.setClaws(2);
        kitty = kitten;
        System.out.println("Value in makeNewCat scope of kitten " + kitten.getClaws());
        //Prints 2. the value pointed to by 'kitten' is 2
        System.out.println("Value in makeNewcat scope of kitty " + kitty.getClaws());
        //Prints 2. The local copy is being used within the scope of this method.
    }
}

class Cat {

    private int claws;

    public void setClaws(int i) {
        claws = i;
    }

    public int getClaws() {
        return claws;
    }
}

这可以在Ideone.com上运行。


11

Java不像C语言一样具有指针(Pointer)概念,但它允许你在堆(heap)上创建新的对象,并由变量引用。没有指针是为了防止Java程序非法引用内存地址,同时也使得Java虚拟机(JVM)能够自动进行垃圾回收(Garbage Collection)。


10
您可以使用Unsafe类来使用地址和指针。 但是正如其名称所示,这些方法是不安全的,通常不是一个好主意。 如果使用不正确,可能导致您的JVM随机死亡(实际上与在C/C++中不正确使用指针相同的问题)。
虽然您可能习惯于指针并认为您需要它们(因为您不知道如何以其他方式编码),但您会发现您不需要它们,而且不使用它们会更好。

9
指针非常强大且实用。在某些情况下,没有指针(如Java)会使代码效率大打折扣。仅因为人们不善于使用指针并不意味着编程语言应该将它们排除在外。难道我就不应该拥有步枪,因为其他人(如军队)使用它们来杀人吗? - Ethan Reesor
2
在过去的40年中,我编写的许多代码都无法在Java中实现,因为Java缺乏核心的低级功能。 - Danger
1
@Danger 这就是为什么许多库使用Unsafe,而在Java 9中删除它的想法引发了如此激烈的争论。计划提供替代方案,但它们尚未准备好。 - Peter Lawrey
1
这实际上是唯一正确的答案。我正要写一个指向https://www.baeldung.com/java-unsafe的答案。 - PeMa
1
大约5年前,我编写了一个库来包装大部分这些功能,以使其更安全。https://github.com/OpenHFT/Chronicle-Bytes - Peter Lawrey
显示剩余6条评论

5

从技术上讲,所有的Java对象都是指针。但是所有的原始类型都是值。没有办法手动控制这些指针。Java内部只使用按引用传递。


1
甚至不通过JNI吗?我对Java没有什么了解,但我听说可以通过这种方式进行一些低级位操作。 - David Seiler
就我的大约4年的Java经验和自己寻找答案而言,Java指针是不可外部访问的。 - Poindexter
谢谢回复。我之所以问是因为我的讲师告诉我,在使用 Java 的手持设备的研究级别上,他们使用指针...这就是我问的原因。 - user213038
如果处于研究级别的话,根据正在进行的工作,可能需要这样的功能,因此他们违反了正常的语言习惯并实现了它们。如果您希望拥有正常 Java 的超集(或子集或混合),则可以实现自己的 JVM,但这样的代码可能无法在其他 Java 平台上运行。 - San Jacinto
@san:感谢您的回答。顺便说一下,我无法投票...显示需要15个声望。 - user213038
从技术上讲,Java 对象不是指针。它们是引用。如果它们是指针,并且您的垃圾回收运行了,那么您的程序将立即崩溃。 - Danger

2
不是很准确,Java没有指针。如果你真的想要的话,可以尝试通过围绕反射构建来模拟它们,但这将具有指针的所有复杂性,却没有任何好处。Java没有指针,因为它不需要它们。你对这个问题有什么期望的答案呢?你是希望能够用它们做些什么还是只是出于好奇?

我很好奇想知道。谢谢你的回答。 - user213038
Java不需要指针,这就是为什么Java添加了JNI的原因 - 为了绕过在Java中无法执行的“类似指针”的或更低级别的函数,无论你编写什么代码都无法实现。 - Danger

2
所有Java对象在传递给函数时都是通过引用复制而非值复制(除了基本类型)。这意味着您发送的是原始对象指针的副本,而不是对象本身的副本。如果需要示例理解,请在下方留言。

1
这是完全错误的。你不能在Java中编写一个可以交换两个对象的swap函数。 - Michael Tsang
@MichaelTsang他在哪里谈到了“swap”?!这个答案没有任何问题。你复制的是引用,而不是对象。这意味着你不能移动对象本身。 - Sebi2020

1
Java 引用类型与 C 指针不同,因为在 Java 中无法有一个指向指针的指针。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接