- 浏览: 109464 次
- 性别:
- 来自: 北京
-
最新评论
-
zhouqiang728:
Myeclipse平台struts+hibernate+spring项目开发示例 -
askyuan:
抄袭csdn上面的
java中四种操作(DOM、SAX、JDOM、DOM4J)xml方式详解与比较 -
yhef:
有没有更详细的JVM资料啊?
JVM的基本工作原理和特点 -
cuiran:
写的很好 ,希望有更多的人能够看到。
一位软件工程师的6年总结 -
llp20_2000:
近来在看利用自定义classloader来加载加密后的cl ...
java类加载内幕
看了这篇文章,相信大家一定回对JAVA的参数传递熟练掌握,不会再对应用莫名其妙了.
JAVA的参数传递,基本类型是把实参赋给形参, 对象是COPY一个副本堆栈里.
很多Java程序员只求追随新生的事物,却连基本的概念都没有,很明显是基本功不过硬,讨论Java的基本功,下面便是在这个想法下催生出的第一篇文章。
Java基本功——Reference
有这样一种说法,如今争锋于IT战场的两大势力,MS一族偏重于底层实现,Java一族偏重于系统架构。说法根据无从考证,但从两大势力各自的社区力量和图书市场已有佳作不难看出,此说法不虚。于是,事情的另一面让人忽略了。
偏巧,我是一个喜欢探究底层实现的Java程序员,虽然我的喜好并非纯正咖啡,剑走偏锋却别是一番风味。
Reference
Java世界泰山北斗级大作《Thinking In Java》切入Java就提出“Everything is Object”。在Java这个充满Object的世界中,reference是一切谜题的根源,所有的故事都是从这里开始的。
Reference是什么?
如果你和我一样在进入Java世界之前曾经浪迹于C/C++世界,就一定不会对指针陌生。谈到指针,往日种种不堪回首的经历一下子涌上心头,这里不是抱怨的地方,让我们暂时忘记指针的痛苦,回忆一下最初接触指针的甜蜜吧!还记得你看过的教科书中,如何讲解指针吗?留在我印象中的一种说法是,指针就是地址,如同门牌号码一样,有了地址,你可以轻而易举找到一个人家,而不必费尽心力的大海捞针。
C++登上历史舞台,reference也随之而来,容我问个小问题,指针和reference区别何在?我的答案来自于在C++世界享誉盛名的《More Effective C++》。
- 没有null reference。
- reference必须有初值。
- 使用reference要比使用指针效率高。因为reference不需要测试其有效性。
- 指针可以重新赋值,而reference总是指向它最初获得的对象
设计选择:
当你指向你需要指向的某个东西,而且绝不会改指向其它东西,或是当你实作一个运算符而其语法需要无法有指针达成,你就应该选择reference。其它任何时候,请采用指针。
这和Java有什么关系?
初学Java,鉴于reference的名称,我毫不犹豫的将它和C++中的reference等同起来。不过,我错了。在Java中,reference可以随心所欲的赋值置空,对比一下上面列出的差异,就不难发现,Java的reference如果要与C/C++对应,它不过是一个穿着reference外衣的指针而已。
于是,所有关于C中关于指针的理解方式,可以照搬到Java中,简而言之,reference就是一个地址。我们可以把它想象成一个把手,抓住它,就抓住了我们想要操纵的数据。如同掌握C的关键在于掌握指针,探索Java的钥匙就是reference。
一段小程序
我知道,太多的文字总是令人犯困,那就来段代码吧!
public class ReferenceTricks {
public static void main(String[] args) {
ReferenceTricks r = new ReferenceTricks();
// reset integer
r.i = 0;
System.out.println("Before changeInteger:" + r.i);
changeInteger(r);
System.out.println("After changeInteger:" + r.i);
// just for format
System.out.println();
// reset integer
r.i = 0;
System.out.println("Before changeReference:" + r.i);
changeReference(r);
System.out.println("After changeReference:" + r.i);
}
private static void changeReference(ReferenceTricks r) {
r = new ReferenceTricks();
r.i = 5;
System.out.println("In changeReference: " + r.i);
}
private static void changeInteger(ReferenceTricks r) {
r.i = 5;
System.out.println("In changeInteger:" + r.i);
}
public int i;
}
对不起,我知道,把一个字段设成public是一种不好的编码习惯,这里只是为了说明问题。
如果你有兴趣自己运行一下这个程序,我等你!
OK,你已经运行过了吗?结果如何?是否如你预期?下面是我在自己的机器上运行的结果:
Before changeInteger:0
In changeInteger:5
After changeInteger:5
Before changeReference:0
In changeReference: 5
After changeReference:0
这里,我们关注的是两个change——changeReference和changeInteger。从输出的内容中,我们可以看出,两个方法在调用前和调用中完全一样,差异出现在调用后的结果。
糊涂的讲解
先让我们来分析一下changeInteger的行为。
前面说过了,Java中的reference就是一个地址,它指向了一个内存空间,这个空间存放着一个对象的相关信息。这里我们暂时不去关心这个内存具体如何排布,只要知道,通过地址,我们可以找到r这个对象的i字段,然后我们给它赋成5。既然这个字段的内容得到了修改,从函数中返回之后,它自然就是改动后的结果了,所以调用之后,r对象的i字段依然是5。下图展示了changeInteger调用前后内存变化。
Reference +--------+ Reference +--------+
---------->| i = 0 | ---------->| i = 5 |
|--------| |--------|
| Memory | | Memory |
| | | |
| | | |
+--------+ +--------+
调用changeInteger之前 调用changeInteger之后
让我们把目光转向changeReference。
从代码上,我们可以看出,同changeInteger之间的差别仅仅在于多了这么一句。
r = new ReferenceTricks();
这条语句的作用是分配一块新的内存,然后将r指向它。
执行完这条语句,r就不再是原来的r,但它依然是一个ReferenceTricks的对象,所以我们依然可以对这个r的i字段赋值。到此为止,一切都是那么自然。
Reference +--------+ +--------+
---------->| i = 0 | | i = 0 |
|--------| |--------|
| Memory | | Memory |
| | Reference |--------|
| | ---------->| i = 5 |
+--------+ +--------+
调用changeReference之前 调用changeReference之后
顺着这个思路继续下去的话,执行完changeReference,输出的r的i字段,那么应该是应该是新内存中的i,所以应该是5。至于那块被我们抛弃的内存,Java的GC功能自然会替我们善后的。
事与愿违。
实际的结果我们已经看到了,输出的是0。
肯定哪个地方错了,究竟是哪个地方呢?
参数传递的秘密
知道方法参数如何传递吗?
记得刚开始学编程那会儿,老师教导,所谓参数,有形式参数和实际参数之分,参数列表中写的那些东西都叫形式参数,在实际调用的时候,它们会被实际参数所替代。
编译程序不可能知道每次调用的实际参数都是什么,于是写编译器的高手就出个办法,让实际参数按照一定顺序放到一个大家都可以找得到的地方,以此作为方法调用的一种约定。所谓“没有规矩,不成方圆”,有了这个规矩,大家协作起来就容易多了。这个公共数据区,现在编译器的选择通常是“栈”,而所谓的顺序就是形式参数声明的顺序。
显然,程序运行的过程中,作为实际参数的变量可能遍布于内存的各个位置,而并不一定要老老实实的呆在栈里。为了守“规矩”,程序只好将变量复制一份到栈中,也就是通常所说的将参数压入栈中。
打起精神,谜底就要揭晓了。
我刚才说什么来着?将变量复制一份到栈中,没错,“复制”!
这就是所谓的值传递。
C语言的旷世经典《The C Programming Language》开篇的第一章中,谈到实际参数时说,“在C中,所有函数的实际参数都是传‘值’的”。
马上会有人站出来,“错了,还有传地址,比如以指针传递就是传地址”。
不错,传指针就是传地址。在把指针视为地址的时候,是否考虑过这样一个问题,它也是一个变量。前面的讨论中说过了,参数传递必须要把参数压入栈中,作为地址的指针也不例外。所以,必须把这个指针也复制一份。函数中对于指针操作实际上是对于这个指针副本的操作。
Java的reference等于C的指针。所以,在Java的方法调用中,reference也要复制一份压入堆栈。在方法中对reference的操作就是对这个reference副本的操作。
谜底揭晓
好,让我们回到最初的问题上。
在changeReference中对于reference的赋值实际上是对这个reference的副本进行赋值,而对于reference的本尊没有产生丝毫的影响。
回到调用点,本尊醒来,它并不知道自己睡去的这段时间内发生过什么,所以只好当作什么都没发生过一般。就这样,副本消失了,在方法中对它的修改也就烟消云散了。
也许你会问出这样的问题,“听了你的解释,我反而对changeInteger感到迷惑了,既然是对于副本的操作,为什么changeInteger可以运作正常?”
呵呵,很有趣的大脑短路现象。
好,那我就用前面的说法解释一下changeInteger的运作。
所谓复制,其结果必然是副本完全等同于本尊。reference复制的结果必然是两个reference指向同一块内存空间。
虽然在方法中对于副本的操作并不会影响到本尊,但对内存空间的修改确实实实在在的。
回到调用点,虽然本尊依然不知道曾经发生过的一切,但它按照原来的方式访问内存的时候,取到的确是经过方法修改之后的内容。
于是方法可以把自己的影响扩展到方法之外。
多说几句
这个问题起源于我对C/C++中同样问题的思考。同C/C++相比,在changeReference中对reference赋值可能并不会造成什么很严重的后果,而在C/C++中,这么做却会造成臭名昭著的“内存泄漏”,根本的原因在于Java拥有了可爱的GC功能。即便这样,我仍不推荐使用这种的手法,毕竟GC已经很忙了,我们怎么好意思再麻烦人家。
在C/C++中,这个问题还可以继续引申。既然在函数中对于指针直接赋值行不通,那么如何在函数中修改指针呢?答案很简单,指针的指针,也就是把原来的指针看作一个普通的数据,把一个指向它的指针传到函数中就可以了。
同样的问题到了Java中就没有那么美妙的解决方案了,因为Java中可没有reference的reference这样的语法。可能的变通就是将reference进行封装成类。至于值不值,公道自在人心。
参考文献
1 《Thinking in Java》
2 《More Effective C++》
3 《The C Programming Language》
发表评论
-
多级反向代理[Squid]下获取客户端真实IP地址
2008-12-04 13:47 2300多级反向代理[Squid]下获 ... -
java Singleton模式
2008-10-05 23:18 989单例模式的好处就是:类只实例化一次,省资源,节省开销,提高速度 ... -
Hibernate的关联关系映射
2008-10-04 23:57 963前言 以下所有描述以选课系统这个案例来说明。在一个选课系统中, ... -
java类加载内幕
2008-10-04 23:55 1162Binildas;purplerain原文地址:http:// ... -
JAVA功底之基础知识精华总结 (各类修饰符)
2008-10-04 23:45 18941、 对象的初始化 (1) 非静态对象的初始化 在创建对 ... -
Java中static、this、super、final用法简谈
2008-10-04 23:41 896本篇旨在帮助准备学习Java以及刚接触Java的朋友认识、掌 ... -
为什么Hibernate如此成功
2008-10-04 23:40 765下面是对Hibernate开发工 ... -
程序员必看:关于SQL Server的若干注意事项
2008-10-04 23:39 877如果你正在负责一个基 ... -
JDBC编程基础
2008-10-04 23:36 9311. 介绍许多开发者和用户都在寻找Java程序中访问数据库的便 ... -
JAVA中最为关键的几个知识点
2008-10-04 23:35 828想成为JAVA高手,你至少 ... -
彻底明白Java的IO系统
2008-10-04 23:34 728一. Input和Output1. stream代表的是任何有 ... -
一位软件工程师的6年总结
2008-10-03 18:49 1067一位软件工程师的6年总 ... -
JVM的基本工作原理和特点
2008-10-03 18:22 2171JVM的基本工作原理和特点 操作系统装入jvm是通过jdk中j ... -
信息推拉模式
2008-10-01 11:09 1367信息推拉模式 信息推拉技术是将信息推送与拉取两种模式结合, ... -
Java的算法题
2008-06-25 17:24 2369/** *//**父子俩的年龄:父亲今年30岁,儿子今年6岁 ... -
Java多线程编程详解
2008-06-24 18:44 1033Java多线程编程详解 ... -
java中四种操作(DOM、SAX、JDOM、DOM4J)xml方式详解与比较
2008-06-23 11:37 36661.详解 1)DOM(JAXP Crimson解析器) DOM ... -
java连接SQL Server数据库
2006-09-03 11:36 2180JSP连接SQL Server数据库 1. ... -
Java IOC框架 挑战Spring
2007-03-17 17:44 839Guice 能够注入 constructors, fields ... -
十年编程经验凝结 与新人们分享
2007-05-11 18:57 648有天和朋友聊天,朋友是国内一家大型互联网企业的一位技术主 ...
相关推荐
Java The Complete Reference, 11th Edition 9781260440232 c.pdf Java The Complete Reference, 11th Edition 9781260440232 c.pdf Java The Complete Reference, 11th Edition 9781260440232 c.pdf
Java: The Complete Reference, Eleventh Edition By 作者: Herbert Schildt ISBN-10 书号: 1260440230 ISBN-13 书号: 9781260440232 Edition 版本: 11 出版日期: 2018-12-12 pages 页数: (1955) The Definitive ...
Java The Complete Reference 10th Edition Java9 编程官方参考(第10版) 带书签 文字版
Java是当今广泛使用的编程语言之一,由Sun Microsystems公司(现为甲骨文公司)于1995年发布。Java具有简单、面向对象、分布式、解释执行、健壮、安全、体系结构中立、可移植、高性能、多线程和动态等特点。Java可以...
Java in a Nutshell Java Language Reference Java AWT Reference Java Fundamental Classes Reference Exploring Java Combined Index Combined Search Web Version Credits
在Neo4j-java-reference-3.3这份参考指南中,涵盖了多个关于如何在Java环境中使用和扩展Neo4j数据库的高级主题。文档内容涉及到如何嵌入Neo4j到Java应用中、使用Neo4j的遍历框架、手动索引、事务管理、在线备份以及...
Your author likes to read reference manuals (believe it or not)—at least if they are reasonably complete—on the grounds that this is the most efficient way to absorb a language quickly.
在Java编程语言中,"reference"是一个至关重要的概念,它涉及到对象的引用和内存管理。在Java的世界里,"Everything is Object",这意味着所有的数据都以对象的形式存在,而reference则是连接这些对象与程序代码的...
### Neo4j Java Reference 3.0:深入理解图数据库扩展与高级应用 #### 概述 《Neo4j Java Reference 3.0》是一本详细介绍如何使用Java语言来开发和扩展Neo4j图数据库的专业指南。本书不仅覆盖了Neo4j的核心功能,...
《Java The Complete Reference 第九版源代码》是一个深入学习Java编程语言的重要资源。这个压缩包包含的源代码是作者在书中讲解各种Java概念和技术时所使用的示例程序,旨在帮助读者更好地理解和应用Java编程知识。...
"jvisualvm"是Java的标准工具之一,它是Java虚拟机监控和诊断的利器。jvisualvm提供了丰富的功能,包括查看和分析JVM的内存状态、线程状态、CPU使用率、类加载情况等。通过这个工具,开发者可以定位内存泄漏、分析...
《Java The Complete Reference》是Java编程领域的一本权威著作,其第八版主要涵盖了基于Java 7的全面知识。这本书详细解析了Java语言的各种...这本书是Java程序员必备的参考书之一,对于初学者和进阶者都极具价值。
Java reference sheet
Java Quick Syntax Reference(2nd) 英文无水印原版pdf 第2版 pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看...
Oracle 9i Support for JavaServer Pages Reference Release 2 (9.2) Oracle 9i Support for JavaServer Pages Reference Release 2 (9.2) 是 Oracle 公司发布的一份关于 JavaServer Pages(JSP)的参考指南,该...