Java中为何存在指针?深入解析Java对象引用与指针的关系
大家都清楚,在 java 中不存在指针。那为什么这里又要提及 java 中的“指针”呢?我们要明白,在 C/C++中,指针会指向内存里的地址,而这个地址就是用来存储变量的值的。这个地址所存储的变量值是“公有”的,这里所说的“公有”是针对拥有该地址的变量来说的。它们都能够访问该地址的内容,同时也可以对其进行修改。一旦进行修改,那么所有指向该地址的变量值都会发生改变。那么在 Java 当中到底有没有指针呢?在 Java 里面是没有指针的定义的。然而,Java 里面的“指针”却随处可见。接下来将进行介绍。我们清楚,在 JAVA 里面使用得最多的就是对象,我们常常会把一个对象传递给不同的变量,比如:

Student s1 = new Student();
Student s2 = s1;、
此时 s1 和 s2 指向同一个对象,即 s1 与 s2 引用相同对象。我们知晓在 java 里创建一个对象会存放在堆中,当进行 new 一个对象操作时,实际上是在堆中开辟了一个用于存储该对象的空间,返回的是存储该对象在堆中的地址。所以在 java 中,我们所说的对象引用就是指针,只是不像 C/C++那样给出了一个明确的定义。java 为了避免指针在使用上带来的麻烦,于是就用对象的引用来替代指针。在上面的例子里,我们在 s1 中对 Student 的属性进行修改。这样一来,当 s2 去获取 Student 中属性的值时,就会得到 s1 修改后的那个值。这是由于 s1 和 s2 都指向堆中的同一块内容。所以,只要其中一个变量对堆中的值进行了修改,其他变量就会获取到堆中最后更新的值。所以我们能够得知,s2 = s1 并非是在堆中又创建了一块内存,仅仅是将一个对象的地址赋予了 s2 。在这个时候,依然只是创建了一个对象,并非两个对象。在实际的开发过程中,常常会遇到这样的情况。例如创建一个 Map 类型的对象,其 value 是一个对象,而 key 是一个字符串,定义如下:
class Student{
private String name;
private int age;
public void setAge(int age)
{
this.age = age;
}
public Student(String name)
{
this.name = name;
}
}
Map studentMap = new HashMap();
创建了一个名为 s1 的学生对象,该对象的姓名是“张三”。
创建了一个名为 s2 的学生对象,这个学生对象的名字是“李四”。
studentMap.put(s1.name,s1);
studentMap.put(s2.name,s2);
此处有一个学生与一个学生相对应,因此这里的 Map 的 key 为学生姓名,其 value 是一个学生对象。将一个 Student 对象的引用赋给了 Map 中 key 对应的 value 。所以,当程序在其他地方改变了 s1 或 s2 的值后,无需将 s1 和 s2 的信息更新到 studentMap 中,studentMap 就能获得 key 对应的 value 的最新值。例如,在程序的某一处将 s1 的 age 修改为“23”,无需将 s1 的最新信息更新到 studentMap 中,studentMap 就会获得 s1 的最新值。因为已经把 s1 对象的引用赋予了 studentMap 中张三所对应的 value,也就是把对象在堆中的地址给了 value。所以只要 s1 有更新,studentMap 就能获得最新的值。
有时候这种状况会对我们程序的执行产生影响,并且与我们原本的设想存在偏差。当我们需要在某一时刻保存一个对象的状态时,通常的理解是把此时的对象赋予一个变量来进行存储。然而,在 Java 里面,这样做是不行的。这样直接把该对象赋给另一个变量,实际上只是把该对象的引用赋给了此变量。此变量的值无法一直保持在当前该对象的值上,会随着程序的执行以及该对象的改变而发生变化。所以无法实现我们的目的。接下来举个例子来说明如何解决该现象:
class Student{
private String name;
private int age;
public void setAge(int age)
{
this.age = age;
}
public Student(String name)
{
this.name = name;
}
创建一个公共的 Student 类,该类接收一个 Student 类型的参数 student 。 这个构造函数用于初始化一个新的 Student 对象,其属性值来自传入的 student 对象。 它可以方便地在创建新的 Student 实例时,复制或初始化已有 Student 对象的状态。 例如,当需要创建一个与另一个 Student 具有相同属性的新对象时,可以使用这个构造函数。 这样可以提高代码的复用性和可维护性。
{
this.name = student.name;
this.age = student.age;
}
}
上面是 student 类,基本与之前相同,只是新增了一个构造函数叫 Student(Student student)。为何要添加这样一个函数呢?这是为了满足我们之前的需求,即保存对象在某个时刻的状态。我们知道,直接将一个对象赋给一个变量,会存在对象的引用,无法保存对象某时刻的状态。同时知晓,若要保存一个对象,需在堆中开辟空间来存放该对象。实际上,上述需求可视为,当要保存某时刻下对象的状态时,也就是该对象的状态不会因后续对象的变化而改变,那么为何不将当前某时刻下对象的状态属性全部复制到一个新对象中呢!如此一来,保存下来的属性就不会受到后续对象变化的影响了。就上面的例子,举例说明:
创建了一个名为 s1 的 Student 对象,其姓名为“王麻子”。
s1.age=23;
........
//此时要保存s1此刻的状态
Student s2 = new Student(s1);
此时 s1 不再指向堆中的一个地址,s2 也不是直接将 s1 赋给它的。s2 是通过 new 操作在堆中开辟出一个空间来存储当前 new 的对象,所以 s1 和 s2 不是指向堆中的一个地址了。上面已经提到 new 能够在堆中开辟一个存储当前 new 的对象的空间。这种操作能够把 s1 里的状态全部转移到 s2 中。从值的角度来看,此时 s2 和 s1 是相同的。然而在堆中,它们是不一样的,即 s1 不等于 s2,因为 s1 和 s2 的 hashcode 不同。但是 s1 的名字与 s2 的名字相等,即 s1.name.equal(s2.name)为真,并且 s1 的年龄等于 s2 的年龄,即 s1.age = s2.age。这实现了保存某个对象在某个时刻的状态呀。并且不会受到程序执行的影响呢。
有关 java“指针”已全部介绍完毕。实际上,java“指针”即为对象的引用,对象存放在堆中。我们知晓 java 中的内存分为堆内存(heap)和栈内存(stack)。堆是用于存放对象的,栈则用来存放一些数据基本类型的值,像 int、float、double、char 等。此文到此基本结束,如有错误欢迎指出,欢迎评论!
谢谢浏览!此文完毕!