如何在Java中使用指针?


112

我知道Java没有指针,但是我听说可以用指针创建Java程序,而这可由少数Java专家来完成。是真的吗


1
无论如何,在Sun的实现中。
迈克尔·迈尔斯

1
我可以使用该指针的地址

6
Java对象的默认hashCode不是它的指针地址,请仔细重新阅读hashCode的约定,您会注意到内存中的两个不同对象可以具有相同的hashCode值。
阿米尔·阿富汗尼

3
在64位和32位Java中,32位hashCode不是地址。hashCodes不保证唯一。当对象在空间之间移动并且内存被压缩时,对象的位置可以在内存中移动,但是hashCode不变。
彼得·劳瑞

2
您可以使用C ++指针完成90%的操作,也可以使用Java引用完成90%的操作,其余10%可以通过将引用包装在另一个对象中来实现(这并不是我做过的必要)
Richard Tingle

Answers:


243

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;

取消引用null:

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.    

您可以通过首先确保不再需要第二个旧值或为另一个指针分配第二个值来确保此安全。

first = second;
second = third; //OK

请注意,以其他方式给second赋值(NULL,new ...)与潜在错误一样多,并且可能会导致丢失其指向的对象。

OutOfMemoryError当您调用new并且分配器无法分配所请求的单元格时,Java系统将引发异常()。这是非常罕见的,通常是由于失控的递归导致的。

请注意,从语言的角度来看,将对象丢弃到垃圾收集器根本不是错误。这只是程序员需要注意的事情。相同的变量可以在不同的时间指向不同的对象,并且当没有指针引用它们时,旧值将被回收。但是,如果程序的逻辑要求维护对对象的至少一个引用,则将导致错误。

新手经常会犯以下错误。

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中没有。


16
好的答案,但您未能解决一个关键的区别:C具有指针算法。(幸运的是)您无法在Java中做到这一点。
R. Martinho Fernandes

10
我讨厌否决任何答案,特别是流行的答案,但是Java没有指针,您根本无法使用像指针这样的引用。在某些情况下,您只能使用真正的指针才能执行操作-例如,您可以获取或设置过程数据空间中的任何字节。您根本无法在Java中做到这一点。如此多的人似乎不明白指针的真正含义真是太糟糕了,恕我直言,现在我将离开我的肥皂盒,希望您原谅我的小爆发。
危险

5
我想你是说不幸。您为什么认为Java不支持指针算术好呢?
user3462295'9

2
@ user3462295因为人们似乎认为普通民众很难使用它,并且只能通过编写编写编译器或设备驱动程序的忍者代码来理解。
iCodeSometime

4
@危险的第一行“ Java中的所有对象都是引用,您可以像使用指针一样使用它们”。这是可以提供的最佳答案。如果唯一的答案是“否,java没有指针”。该答案将无济于事。相反,此答案说明您可以做什么。
csga5000

46

Java确实有指针。每当使用Java创建对象时,实际上就是在创建指向该对象的指针。然后可以将此指针设置为其他对象或null,并且原始对象仍将存在(等待垃圾回收)。

您在Java中无法做的是指针运算。您不能取消引用特定的内存地址或增加指针。

如果您真的想获得低级,唯一的方法是使用Java本机接口。即使这样,低级部分也必须使用C或C ++完成。


9
Java根本没有简单明了的指针,而且我一生都无法理解为什么这么多的答案都指出了错误的信息。这是StackOverlfow人!在计算机科学中,指针的定义是(您可以在Google上找到):“在计算机科学中,指针是一种编程语言对象,其值引用(或“指向”)存储在计算机内存中其他位置的另一个值,使用它的地址。” Java中的引用不是指针。Java需要运行垃圾回收,如果您有一个真正的指针,那就错了!
危险

3
@Danger指针是一条包含内存地址的数据。Java是两件事,人们会忘记。它是一种语言和平台。就像人们不会说.NET是一种语言一样,我们需要记住,说“ Java没有指针”可能会引起误解,因为该平台当然具有并使用指针。程序员不能通过Java语言访问这些指针,但是它们确实存在于运行时中。语言中的引用位于运行时中实际指针的顶部。
kingfrito_5005

@ kingfrito_5005您似乎同意我的观点。我相信您说过“程序员无法通过Java语言访问那些指针”,这与我关于“ Java没有指针”的说法是一致的。引用在语义上与指针明显不同。
危险

1
@Danger,这是真的,但是我认为在这些情况下区分平台和语言很重要,因为我知道,当我刚开始编写像您这样的声明时,会让我感到困惑。
kingfrito_5005 2015年

1
是的,Java当然在变量后面有指针。只是开发人员不必与他们打交道,因为Java在幕后为此发挥了魔力。这意味着不允许开发人员直接将变量指向特定的内存地址,并处理数据偏移量和填充。它允许Java保持干净的内存垃圾回收,但是由于每个变量本质上都是一个指针,因此还需要一些额外的内存来使所有这些自动生成的指针地址有效,而在更底层的语言中可以避免。
VulstaR

38

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

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


3
这里的几个正确答案之一几乎没有任何投票?
危险

请注意,存在模拟指针行为的“指针”对象。对于许多目的,可以以相同的方式使用此方法,但不能使用内存级别的寻址。考虑到问题的性质,我感到奇怪的是没有其他人提到这一点。
kingfrito_5005

这应该是最重要的答案。因为对于初学者来说,“在后台Java具有指针”可能会导致非常混乱的状态。即使在入门级学习Java时,也要指出指针不安全,这就是为什么Java中不使用指针。需要告知程序员他/她可以看到并继续从事的工作。引用和指针实际上有很大的不同。
Neeraj Yadav

不确定我是否完全同意链接中的“ 2.2.3 No Enums”主题。数据可能已过时,但是Java 确实支持枚举。
Sometowngeek

1
@Sometowngeek链接的文档是1996年的白皮书,描述了Java的第一个版本。枚举是在Java版本1.5(2004)中引入的
-Fortega,

11

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上运行。


10

Java没有C所具有的指针,但是它允许您在堆上创建被变量“引用”的新对象。缺少指针是为了阻止Java程序非法引用内存位置,并且还使Java虚拟机可以自动执行垃圾回收。


7

您可以使用Unsafe类使用地址和指针。但是,顾名思义,这些方法是不安全的,通常是个坏主意。使用不正确会导致JVM随机死亡(实际上,在C / C ++中使用指针不正确会导致相同的问题)

尽管您可能习惯于使用指针并认为自己需要它们(因为您不知道如何以其他任何方式编码),但您会发现自己不使用指针,并且会因此而受益。


8
指针非常强大且有用。在许多情况下,没有指针(Java)会使代码的效率大大降低。仅仅因为人们讨厌使用指针并不意味着语言应该排除它们。我是否应该因为其他人(例如军人)使用步枪杀死人而没有步枪?
伊桑·里索

1
Java并没有像Java语言那样有用或强大。对于其他所有内容,我都很少使用Unsafe类。
彼得·劳瑞

2
我过去40年编写的许多代码都无法用Java实现,因为Java缺少核心的低级功能。
危险

1
@Danger,这就是为什么大量库使用Unsafe的原因,而删除它的想法在Java 9中引起了很多争论。该计划是要提供替代品,但尚未准备好。
彼得·劳瑞

3

从技术上讲,所有Java对象都是指针。尽管所有原始类型都是值。无法对这些指针进行手动控制。Java仅在内部使用按引用传递。


1
甚至不通过JNI?这里没有Java知识,但我想我已经听说您可以通过这种方式完成一些低级的位捕获。
David Seiler

据我大约4年的Java经验,我自己寻找答案时说不,Java指针不可从外部访问。
Poindexter

感谢您的回复。我问是因为,我的讲师告诉我,在研究水平上,手持设备中使用Java的地方,它们使用指针...这就是为什么我问

@unknown(google),如果是处于研究级别,则取决于正在执行的操作,可能是他们需要这样的功能,因此他们违反了常规的习惯用法,无论如何都实施了它们。如果您希望拥有常规Java功能的超集(或子集或混合),则可以实现自己的JVM,但是此类代码可能无法在其他Java平台上运行。
San Jacinto

@san:谢谢你的回答。顺便说一句,我不能投票..说我需要15个声望

2

不是,不是

Java没有指针。如果您真的想要,可以尝试通过围绕反射之类的东西来模拟它们,但是它将具有指针的所有复杂性,而没有任何好处

Java没有指针,因为它不需要它们。您希望从这个问题中得到什么样的答案,即您内心深处希望自己可以将其用于某些事情,还是只是出于好奇?


我很好奇这一点。感谢您的回答

Java确实需要指针,这就是Java添加JNI的原因-为了避免缺少“类似指针的”或较低级别的函数,无论您编写什么代码,都无法在Java中实现。
危险

2

除了原语外,java中的所有对象都通过引用副本传递给函数。

实际上,这意味着您要发送指向原始对象的指针的副本,而不是对象本身的副本。

如果您想举例说明,请发表评论。


这是完全错误的。您不能用swapJava 编写将交换两个对象的函数。
曾荫权

2

Java引用类型与C指针不同,因为Java中没有指向该指针的指针。


1

正如其他人所说,简短的答案是“否”。

当然,您可以编写可与Java指针一起使用的JNI代码。根据您要完成的任务,也许可以使您进入某个位置,也许没有。

您始终可以通过创建数组并使用数组中的索引来模拟点。同样,根据您要完成的工作,这可能有用也可能没有用。


1

摘自Godfrey Nolan的《反编译Android》一书

安全性要求Java中不使用指针,因此黑客无法脱离应用程序进入操作系统。没有指针意味着其他东西(在这种情况下为JVM)必须负责分配和释放内存。内存泄漏也应该成为过去,或者理论如此。一些用C和C ++编写的应用程序因泄漏像筛子一样而臭名昭著,因为程序员没有注意在适当的时候释放不需要的内存,而不是任何人读这本书都会犯这种罪。垃圾回收还应该提高程序员的生产率,减少调试内存问题的时间。


0

您也可以使用文字指针。您必须自己实现它们。对于专家来说,这是非常基本的;)。使用int / object / long / byte和voila数组,您具有实现指针的基础。现在,任何int值都可以是指向该数组int []的指针。您可以递增指针,可以递减指针,可以乘以指针。您确实有指针算术!这是实现1000个int属性类并具有适用于所有属性的通用方法的唯一方法。您也可以使用byte []数组代替int []

但是,我希望Java允许您通过引用传递文字值。沿线的东西

//(* telling you it is a pointer) public void myMethod(int* intValue);


-1

所有的Java对象都是指针,因为持有地址的变量称为指针,而持有对象的地址称为对象。因此,对象是指针变量。


1
不,Java对象不是指针。它们等效于引用,引用具有不同的功能和语义。
危险
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.