Java中对象和对象引用的区别

本文最后更新于:12 天前

对象

对象是什么?

在《Java编程思想》里,是这样解释的:

“按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类’就是‘类型’的同义词。”

所以对象是类的实例。而类就是类型的意思。

对象引用

同样是《Java编程思想》里面的一句话:

“每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。”

用一个例子来理解

A a = new A();

A a则仅仅是对象的声明,是在栈上创建了一个对象引用。

即a是一个对象引用而非对象本身。

而只有当new A()这个操作调用之后,才在堆上创建一个类A的对象,或者说是实例。

而a = new A();才是真正把a这个对象引用和对象本身关联上。

对象和对象引用的区别

下面看一个例子

public class Test{
    public static void main(String[] args){
        int[] a = new int[]{0};
        int b = a[0];
        b = 123;
        System.out.println(a[0]);
    }
}

如果我们认为Java中的引用和C中的指针是同一个东西的话,上面这段Java程序就应该对应的下面这段c++程序

#include <bits/stdc++.h>
using namespace std;
int main()
{
	int a[1]={0};
	int *b = a;
	*b = 123;
	cout<<a[0];
	return 0;
}

这段C++程序的结果当然是123。

但上面第一段Java程序输出结果当然是0而不是123。

这是因为Java中的引用虽然和C++的指针看似好像一样,都是描述变量的工具,但实际上还是有差别的。


Java中的变量只有两种类型,一种是基本数据类型,还有一种是引用类型。

当作为参数传递给一个方法时,处理这两种类型的方式是相同的。Java中都是按值传递,没有引用传递。即:

按值传递意味着传递的是原始值的一个副本。这样,即使函数改变了值,改变的也是原始值的副本,而非原始值本身。

而引用传递则意味着函数接收的就是原始值本身,这样当函数修改这个值,那么对应的原始值也会被修改。

看一个例子

/**
 * @author yinchao
 */
public class NewClassForTest {
    int a;
    String name;
}
import org.junit.jupiter.api.Test;

/**
 * @author yinchao
 */
class TestMethod {
    @Test
    public void test() {
        String string = new String("123");
        NewClassForTest newClassForTest = new NewClassForTest();
        newClassForTest.a = 1;
        newClassForTest.name =  "test1";
        change(string,newClassForTest);
        System.out.println(string);
        System.out.println(newClassForTest.a+"\n"+newClassForTest.name);
    }

    private void change(String newString,NewClassForTest newClassForTest) {
        newString = "1234";
        newClassForTest.a = 2;
        newClassForTest.name = "test2";
    }
}

结果:
123
2
test2

那么在此处为什么同样是通过change方法,string的结果没变,而newClassForTest的改变了?

  • 对于string因为在change方法中,newString这个对象引用已经不再指向原来的对象"123"了

    在 new String的过程中,newString 这个对象引用已经把原来的指向取消,而且建立了一个新的指向到这个新的"1234"。而原来的string还是指向到原来的"123",并没有收到对象引用副本改变指向的影响。

  • 对于newClassForTest,newClassForTest.a = 2;实际上是把对象的属性赋了新的值。函数形参和实参都是对象的引用,而且都是指向同一个对象,和副本并无关系,和是否在函数内也没有关系。这两个newClassForTest并没有主从之分,一个对象引用改变的对象的属性,另一个对象引用的对象当然也会随之改变。