小羽

1 minute read

指针是一种在众多编程语言中非常常见的概念,它的使用非常广泛。实际上,Java为了避免使用上的繁琐,其设计者用引用变量的概念代替了指针,但是实际使用上,两者比较类似,所以本文依旧以指针为题来讲述此类概念。下面我们就来介绍一下指针的概念。

在介绍指针之前,我们首先要弄清楚Java中的两大重要储存空间——堆和栈。为了使用上的方便,堆(Heap)和栈(Stack)分别用来储存不同的东西。下表对比了两者的特点:

储存区域 储存内容 优点 缺点 回收
基本类型的变量和对象的引用变量 存取速度比堆要快,仅次于寄存器,栈数据可以共享 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量 当超过变量的作用域后,Java会自动释放掉该变量,内存空间可以立即被另作他用
由new等指令创建的对象和数组 可以动态地分配内存大小,生存期也不必事先告诉编译器 由于要在运行时动态分配内存,存取速度较慢 由Java虚拟机的自动垃圾回收器来回收不再使用的数据

为了更加形象的解释这些概念,我们不妨看一看下面的例子:

A a = new A();

上述代码是在Java编程中时常会会出现的,我们来好好的分析一下,这样简单的一行代码究竟是做了什么。实际上,这小小的一行代码是做了三件事情,这行代码的前半部分申明了引用变量a为A类型,并将其储存到栈(Stack)当中并分配了一个储存地址给它, 其代码为:

A a;

接着,这行代码创建了一个新的类型为A的对象,这个过程用关键字new来实现,创建的新对象被储存在堆Heap里,并被分配了一个储存地址,其代码为:

new A();

最后,这行代码中的等号”=“将储存在堆里的引用变量a指向了栈里新创建的对象A(),这个指的过程用储存地址来实现,也就是说,等号将两个储存地址配对并连接了起来。

Note: 值得注意的是,在Java中,使用引用变量时,等号”=“并不是给等号左边的变量赋上右边的新值的意思,而是将等号左边的变量指向右边的对象的地址的意思。当人们将一个引用变量先等于对象a,再等于另一个对象b时,并不是将储存在堆(Heap)中的a对象的值改变成了b,而是将引用变量指向对象a的指针断开,接着将指针指向了对象b。下面我们用一个例子来解释这个过程:

在一个足球队中,有很多球员,而只有一个队长。有的时候,我们想要更换队长,这个例子用来实现这个过程。我们首先创建两个球员——洛里和凯恩(对象包括球员的姓,名和踢球的位置):

Player llorris = new Player("Llprris", "Hugo", "Goalkeeper");
Player kane = new Player("Kane", "Harry", "Center Forward");

其实这两行代码实现了如下图的情况:

图片名称

也就是在栈(Stack)中首先申明了引用变量llorris和kane,接着在堆(Heap)中创建了两个Player对象,最后将左边的引用变量指向右边对应的对象。

这时候,我们首先将队长设置为洛里,代码为:

Player captain = llorris;

通过这行代码,我们首先申明了引用变量captain,并将其指向变量llorris指向的对象。其情况如下图所示:

图片名称

接着,我们将队长更改为kane,代码为:

captain = kane;

这时候,captain指向llorris所指向的对象的指针被删除,而一个新的从captain指向kane所指向的对象的指针被创建,其情况如下图所示:

图片名称

堆(Heap)中llorris所指向的对象的值并没有发生改变,只是引用变量captain所指向的对象发生了变化。

如果我们改变引用变量kane所指的对象的值,那么由于引用变量captain所指的对象和kane相同,那么captain所指对象的值也就改变了。比如我们将kane的位置改为Center Back,代码为:

kane.position = "Center Back";

那么,就发生了如下图的变化:

图片名称

所以captain.position的值也会从”Center Forward”变为”Center Back”。

Note:

  1. 当堆(Heap)中的对象没有被栈(Stack)中的引用变量所指时,java虚拟机(JVM)会在之后自动识别到他们,并将它们扔进回收站,从而不再继续占用储存空间。
  2. 当我们将引用变量指向”null”后,这个引用变量原本指向的指针会被断开,且不再指向任何对象,但是JVM不会自动删除它们。

总结

引用变量是Java中非常重要的一个基本概念,在理解上很容易由于”=“的使用和基本变量时不同造成混淆。引用变量如果C语言中的指针,其被储存在栈(Stack)中,可以用”=“将其指向堆”Heap”中的对象。当引用变量再次指向一个新的对象时,原本的指针断开,指向新的对象。当引用变量设置为等于”null”后,它不再指向任何对象。