java reference(转)

http://blog.163.com/[email protected]/blog/static/112987702200962211145825/

在Java中的引用类型,是指除了基本的变量类型之外的所有类型,所有的类型在内存中都会分配一定的存储空间(形参在使用的时候也会分配存储空间,方法调用完成之后,这块存储空间自动消失), 基本的变量类型只有一块存储空间(分配在stack中), 而引用类型有两块存储空间(一块在stack中,一块在heap中), 方法形参的值传递(引用)是指形参和传进来的参数指向同一个值的内存(heap)中;

java是传值还是传引用,这个估计很多人至今都很糊涂,这里有篇文章写的还是可以的,大家可以看看。。 这个写的还是比较清楚,只是不够深入。

1. 简单类型是按值传递的

Java 方法的参数是简单类型的时候,是按值传递的 (pass by value)。这一点我们可以通过一个简单的例子来说明:

/* 例 1 */

/**

* @(#) Test.java

* @author fancy

*/

public class Test {

public static void test(boolean test) {

test = ! test;

System.out.println("In test(boolean) : test = " + test);

}

public static void main(String[] args) {

boolean test = true;

System.out.println("Before test(boolean) : test = " + test);

test(test);

System.out.println("After test(boolean) : test = " + test);

}

}

运行结果:

Before test(boolean) : test = true

In test(boolean) : test = false

After test(boolean) : test = true

不难看出,虽然在 test(boolean) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 main(String[]) 方法里的 test 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。 2. 什么是引用

Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java 中简单类型没有引用。既然争论中提到了引用这个东西,为了搞清楚这个问题,我们必须要知道引用是什么。

简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似 C 语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。

如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间来保存。但是它们的值是相同的,都指示同一个对象在内存的中位置。比如

String a = "Hello";

String b = a;

这里,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 "Hello"。也许你还觉得不够直观,因为 String 对象的值本身是不可更改的 (像 b = "World"; b = a; 这种情况不是改变了 "World" 这一对象的值,而是改变了它的引用 b 的值使之指向了另一个 String 对象 a)。那么我们用 StringBuffer 来举一个例子:

/* 例 2 */

/**

* @(#) Test.java

* @author fancy

*/

public class Test {

public static void main(String[] args) {

StringBuffer a = new StringBuffer("Hello");

StringBuffer b = a;

b.append(", World");

System.out.println("a is " + a);

}

}

运行结果:

a is Hello, World

这个例子中 a 和 b 都是引用,当改变了 b 指示的对象的值的时候,从输出结果来看,a 所指示的对象的值也改变了。所以,a 和 b 都指向同一个对象即包含 "Hello" 的一个 StringBuffer 对象。

这里我描述了两个要点:

1. 引用是一种数据类型,保存了对象在内存中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象);

2. 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。

3. 对象是如何传递的呢

关于对象的传递,有两种说法,即“它是按值传递的”和“它是按引用传递的”。这两种说法各有各的道理,但是它们都没有从本质上去分析,即致于产生了争论。 既然现在我们已经知道了引用是什么东西,那么现在不妨来分析一下对象作是参数是如何传递的。还是先以一个程序为例:

/* 例 3 */

/**

* @(#) Test.java

* @author fancy

*/

public class Test {

public static void test(StringBuffer str) {

str.append(", World!");

}

public static void main(String[] args) {

StringBuffer string = new StringBuffer("Hello");

test(string);

System.out.println(string);

}

}

运行结果:

Hello, World!

test(string) 调用了 test(StringBuffer) 方法,并将 string 作为参数传递了进去。这里 string 是一个引用,这一点是勿庸置疑的。前面提到,引用是一种数据类型,而且不是对象,所以它不可能按引用传递,所以它是按值传递的,它么它的值究竟是什么呢?是对象的地址。

由此可见,对象作为参数的时候是按值传递的,对吗?错!为什么错,让我们看另一个例子:

/* 例 4 */

/**

* @(#) Test.java

* @author fancy

*/

public class Test {

public static void test(String str) {

str = "World";

}

public static void main(String[] args) {

String string = "Hello";

test(string);

System.out.println(string);

}

}

运行结果:

Hello

为什么会这样呢?因为参数 str 是一个引用,而且它与 string 是不同的引用,虽然它们都是同一个对象的引用。str = "World" 则改变了 str 的值,使之指向了另一个对象,然而 str 指向的对象改变了,但它并没有对 "Hello" 造成任何影响,而且由于 string 和 str 是不同的引用,str 的改变也没有对 string 造成任何影响,结果就如例中所示。

其结果是推翻了参数按值传递的说法。那么,对象作为参数的时候是按引用传递的了?也错!因为上一个例子的确能够说明它是按值传递的。

结果,就像光到底是波还是?问题,其答案就只能是:即是按值传递也是按引用传递,只是参照物不同,结果也就不同。

4. 正确看待传值还是传引用的问题

要正确的看待这个问题必须要搞清楚为什么会有这样一个问题。

实际上,问题来源于 C,而不是 Java。

C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针,它们的区别,可以用一个简单的例子说明: /* 例 5 */

/**

* @(#) test.c

* @author fancy

*/

void SwapValue(int a, int b) {

int t = a;

a = b;

b = t;

}

void SwapPointer(int * a, int * b) {

int t = * a;

* a = * b;

* b = t;

}

void main() {

int a = 0, b = 1;

printf("1 : a = %d, b = %d\n", a, b);

SwapValue(a, b);

printf("2 : a = %d, b = %d\n", a, b);

SwapPointer(&a, &b);

printf("3 : a = %d, b = %d\n", a, b);

}

运行结果:

1 : a = 0, b = 1

2 : a = 0, b = 1

3 : a = 1, b = 0

大家可以明显的看到,按指针传递参数可以方便的修改通过参数传递进来的值,而按值传递就不行。

当 Java 成长起来的时候,许多的 C 程序员开始转向学习 Java,他们发现,使用类似 SwapValue 的方法仍然不能改变通过参数传递进来的简单数据类型的值,但是如果是一个对象,则可能将其成员随意更改。于是他们觉得这很像是 C 语言中传值/传指针的问题。但是 Java 中没有指针,那么这个问题就演变成了传值/传引用的问题。可惜将这个问题放在 Java 中进行讨论并不恰当。

讨论这样一个问题的最终目的只是为了搞清楚何种情况才能在方法函数中方便的更改参数的值并使之长期有效。 Java 中,改变参数的值有两种情况,第一种,使用赋值号“=”直接进行赋值使其改变,如例 1 和例 4;第二种,对于某些对象的引用,通过一定途径对其成员数据进行改变,如例 3。对于第一种情况,其改变不会影响到方法该方法以外的数据,或者直接说源数据。而第二种方法,则相反,会影响到源数据——因为引用指示的对象没有变,对其成员数据进行改变则实质上是改变的该对象。

5. 如何实现类似 swap 的方法

传值还是传引用的问题,到此已经算是解决了,但是我们仍然不能解决这样一个问题:如果我有两个 int 型的变量 a 和 b,我想写一个方法来交换它们的值,应该怎么办?

结论很让人失望——没有办法!因此,我们只能具体情况具体讨论,以经常使用交换方法的排序为例:

/** 例 6 */

/**

* @(#) Test.java

* @author fancy

*/

public class Test {

public static void swap(int[] data, int a, int b) {

int t = data[a];

data[a] = data[b];

data[b] = t;

}

public static void main(String[] args) {

int[] data = new int[10];

for (int i = 0; i < 10; i++) {

data[i] = (int) (Math.random() * 100);

System.out.print(" " + data[i]);

}

System.out.println();

for (int i = 0; i < 9; i++) {

for (int j = i; j < 10; j++) {

if (data[i] > data[j]) {

swap(data, i, j);

}

}

}

for (int i = 0; i < 10; i++) {

System.out.print(" " + data[i]);

}

System.out.println();

}

}

运行结果(情况之一):

78 69 94 38 95 31 50 97 84 1

1 31 38 50 69 78 84 94 95 97

swap(int[] data, int a, int b) 方法在内部实际上是改变了 data 所指示的对象的成员数据,即上述讨论的第二种改变参数值的方法。希望大家能够举一反三,使用类似的方法来解决相关问题。

时间: 2024-10-20 11:45:27

java reference(转)的相关文章

Java Reference &amp; ReferenceQueue一览

Overview The java.lang.ref package provides more flexible types of references than are otherwise available, permitting limited interaction between the application and the Java Virtual Machine (JVM) garbage collector. It is an important package, centr

理解java reference

Java世界泰山北斗级大作<Thinking In Java>切入Java就提出“Everything is Object”.在Java这个充满Object的世界中,reference是一切谜题的根源,所有的故事都是从这里开始的. Reference是什么? 如果你和我一样在进入Java世界之前曾经浪迹于C/C++世界,就一定不会对指针陌生.谈到指针,往日种种不堪回首的经历一下子涌上心头,这里不是抱怨的地方,让我们暂时忘记指针的痛苦,回忆一下最初接触指针的甜蜜吧!还记得你看过的教科书中,如何讲

Java Reference Types

References Java provides two different types/classes of Reference Objects: strong and weak. Weak Reference Objects can be further divided into soft and phantom. Strong Reference StringBuilder builder = new StringBuilder(); This is the default type/cl

Java Reference 源码分析

Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使用Reference对象来引用其它对象,但是最后还是会被垃圾收集器回收.程序有时候也需要在对象回收后被通知,以告知对象的可达性发生变更.  Java提供了四种不同类型的引用,引用级别从高到低分别为FinalReference,SoftReference,WeakReference,PhantomReference.其中FinalReference不对外提供使用.每种类型对应着

What Influences Method Call Performance in Java?--reference

reference from:https://www.voxxed.com/blog/2015/02/too-fast-too-megamorphic-what-influences-method-call-performance-in-java/ Whats this all about then? Let’s start with a short story. I proposed a change on the a Java core libs mailing list to overri

在线学习Java免费资源推荐

你想学习Java吗?来对地方了!这篇文章将会介绍很多高质量的免费资源,包括网页.论坛.电子书和速查表. Java是一种面向对象的编程语言,拥有独立.多线程.安全.动态和健壮的特点.归功于其多功能的特点,Java已经成为最流行的编程语言之一,可以让你开发出健壮的应用程序. Java几乎是所有商务应用程序的核心.它有多种脚本语言和流行的框架,可以开发客户端和服务端.因此,学习Java不仅仅可以提高你的知识储备,也有利于你在事业上的发展. 这篇文章将介绍各种各样的网络资源(不包含必读的Java书籍),

hellojava上 java问题排查经验笔记

前些天发现:http://hellojava.info/这个站点,文章基本阿里对遇到java问题排查分析总结,内容丰富. 线上故障总结其实是最有价值的,好的总结就是一个系统演进历史,是团队难得的积累沉淀. 虽然公众出来的内容不多,但很值得一看,花了不少时间把整个完整每篇文章挨个看一遍,嗯,习惯轮不错的资源. 顺手整理了笔记: 1. Hashmap 并发情况下未加锁导致OOM      嗯,死循环很常见,OOM也会有,序列化时 HashMap.writeObject 一直执行生成巨大的数组. 2.

Java Programming Tutorial Java Native Interface (JNI)

1.  Introduction At times, it is necessary to use native codes (C/C++) to overcome the memory management and performance constraints in Java. Java supports native codes via the Java Native Interface (JNI). JNI is difficult, as it involves two languag

Java学习资源

最新的科技一般都是先有英文的,所以英语有多重要可想而知.我的英文很烂,一直想学,从来都是说起来容易,想起来简单,做起来最难.只能强迫自己多看些英语方面的技术网站,技术英语两不误. 学习 Java 最好的电子书(PDF) 喜欢阅读的可以通过这些免费的 Java 电子进行自学.大多数在线的电子书都是更新的,完整的.覆盖了 Java 的大多数细节. Official Java Tutorial by Oracle (Sun) 这是 Addison-Wesley 出版社的官方 Java 指南. Java