java引用传递怎么写_聊聊Java里面的引用传递

```

class Dog{

public String name;

public Dog() {

}

public Dog(String name) {

this.name = name;

}

}

```

然后,我们试着创建一个Dog对象:

```

Dog dog=null; //1

System.out.println(dog.name);

dog=new Dog();//2

```

然后运行一下,我们会看到一个异常:

```

Exception in thread "main" java.lang.NullPointerException

```

注意这个异常,叫空指针异常,在Java里面任何对象没有初始化的时候,如果我们使用其内部属性,就会抛出上面的信息,这也从侧面反映了dog这个变量的作用,其实就是指针,而并非引用。对于已经实例化的对象,如果我们没有重写toString方法,打印指针会得到一串类名组合+内存地址转换的十六进制字符串。

我们接着来看一个例子:

```

public static void change(String point){

point="new value";

}

public static void swapString(){

String orgin="orgin";

System.out.println(orgin);

change(orgin);

System.out.println("==================after==================");

System.out.println(orgin);

}

```

如果在main方法里面,执行swapString()方法,输出的结果都是orgin。

对于Dog对象也一样,如下:

```

public static void change(Dog dog){

dog=new Dog("CAT");

dog.name="cat";

}

public static void swapDog(){

Dog dog=new Dog("tom");

System.out.println(dog.name);

change(dog);

System.out.println("==================after==================");

System.out.println(dog.name);

}

```

最终输出的结果都是tom,好像从未执行过change方法一样。

```

tom

==================after==================

tom

```

这是为什么? 你可能要说很简单啊,方法里面的作用域,只在方法里生效,出了方法就无效了。真的是这样吗? 接着看下面,我们稍微改动一下change方法:

```

public static void change(Dog dog){

dog.name="new_tom";

dog=new Dog("CAT");

dog.name="cat";

}

```

同样,在main方法执行,输出的结果是:

```

tom

==================after==================

new_tom

```

这里面,如果不理解值传递(指针传递)和引用传递的区别,其实是很难明白原因的:

```

public static void change(Dog dog){

dog=new Dog("CAT");//3 memory location:8888

dog.name="cat";//4

}

public static void swapDog(){

Dog dog=new Dog("tom");//1 memory location:7777

System.out.println(dog.name);

change(dog);//2

System.out.println("==================after==================");

System.out.println(dog.name);

}

```

我们加上序号之后,来分析一下,首先在第一步,我们创建了一个Dog对象,其中dog变量是指针(或者按Java的习惯叫引用,但其实是不正确的),指针存的是内存地址,而并非是内存中的数据本身,我们假设内存地址是7777,然后在第二步,将dog(指针)引用,传给了change方法,在第三步,因为我们新创建了一个Dog,并把dog指针指向了新的对象,这里假设其在堆上的内存地址是8888,然后并给内存地址=8888的Dog对象的name赋值了cat,然后方法执行结束,最后回到swapDog方法,继续执行,此时swapDog方法dog对象的指针仍然是7777,所以并没有任何变化。

图示如下:

20b81a4ecc55b294c6171959e1054504.gif

注意change方法执行后,没有指针指向内存地址8888的对象,故会在下一次gc时回收。

接着我们分析下,微调后的change方法:

```

public static void change(Dog dog){

dog.name="new_tom";// 2.1

dog=new Dog("CAT");

dog.name="cat";

}

```

在第2.1步,我们通过dog指针=7777的数据,重新改变了其名称,这意味着内存地址7777的数据,被修改了,后面的两行改的是内存地址=8888的数据,所以最终结果是改变后的。

注意,如果Java语言是引用传递的话,那么最终的结果name肯定是cat,因为引用传递的修改,指的就是数据本身,而并非地址。

上面关于值传递(指针传递)和引用传递,说的有点抽象,我打个比方:

指针指的是你的名字,通过指针可以找到数据本身,然后操作数据,但如果指针本身(非数据本身)变了,也就是你名字变了,但其实跟你没有关系,你自己还是你自己,你可以重新在换个名字代表你。

引用指的是你本身,如果本身变了,比如你换了个发型,这个时候,无论你换多个名字(指针),你都一样是你,改变不了你发型换了的事实。

所以,这个时候如果按照值传递(指针传递)的理解,来看上面的例子,你就会恍然大悟。在change方法里面dog的指针已经被替换成了8888,而8888地址代表的是新的对象

所以不会改变7777的对象的内容,在微调后的版本中,我们直接改变了7777地址数据的name,所以最终的结果也是改变后的。

总结:

Java语言本身是值传递,也叫做指针传递,虽然我们一直叫引用类型,但其实它实际上是一个指针,而真正的引用传递改变的是数据本身的内容,如Lisp和Fortran语言,无论哪种方式,我们只要理解了其本质,就可以掌握的更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值