- 浏览: 2147121 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (682)
- 软件思想 (7)
- Lucene(修真篇) (17)
- Lucene(仙界篇) (20)
- Lucene(神界篇) (11)
- Solr (48)
- Hadoop (77)
- Spark (38)
- Hbase (26)
- Hive (19)
- Pig (25)
- ELK (64)
- Zookeeper (12)
- JAVA (119)
- Linux (59)
- 多线程 (8)
- Nutch (5)
- JAVA EE (21)
- Oracle (7)
- Python (32)
- Xml (5)
- Gson (1)
- Cygwin (1)
- JavaScript (4)
- MySQL (9)
- Lucene/Solr(转) (5)
- 缓存 (2)
- Github/Git (1)
- 开源爬虫 (1)
- Hadoop运维 (7)
- shell命令 (9)
- 生活感悟 (42)
- shell编程 (23)
- Scala (11)
- MongoDB (3)
- docker (2)
- Nodejs (3)
- Neo4j (5)
- storm (3)
- opencv (1)
最新评论
-
qindongliang1922:
粟谷_sugu 写道不太理解“分词字段存储docvalue是没 ...
浅谈Lucene中的DocValues -
粟谷_sugu:
不太理解“分词字段存储docvalue是没有意义的”,这句话, ...
浅谈Lucene中的DocValues -
yin_bp:
高性能elasticsearch ORM开发库使用文档http ...
为什么说Elasticsearch搜索是近实时的? -
hackWang:
请问博主,有用solr做电商的搜索项目?
Solr中Group和Facet的用法 -
章司nana:
遇到的问题同楼上 为什么会返回null
Lucene4.3开发之第八步之渡劫初期(八)
长久以来,在Java语言里面一直有一个争论,就是Java语言到底是值传递(pass-by-value)还是引用传递(pass-by-reference),有的人说是值传递,有的人说是引用传递,两边各执一词,从而误导了很多开发者,更有甚者告诉开发者说不必纠结Java到底是值传递还是引用传递,只要能用就行了,但事实真的是这样吗? 答案是否定的。
为了弄清这两个概念,我们先要理解这两个概念本身的意思:
首先值传递本身这个名字,我感觉就误导了不少人,对于值传递你可以理解成是数据的内存地址传递,并非是数据本身,或者叫它是指针。在维基百科里面,关于指针有清晰的描述:
In computer science, a pointer is a programming language object that stores the memory address of another value located in computer memory. A pointer references a location in memory。
然后,我们看引用传递中的引用的在维基百科上的描述:
In computer science, a reference is a value that enables a program to indirectly access a particular datum, such as a variable's value or a record, in the computer's memory or in some other storage device.
简单的说,引用本身就代表了数据,改变引用相当于改变了数据本身。
根据概念的定义再回到Java语言里面,就会发现对Java本身来说,它只有指针传递也就是值传递,并非是引用传递。到这里,我相信有一部分读者可能已经接受不了,因为在Java里面大多数时候,我们都是讲基本类型,引用类型,从没听过什么指针的概念。导致这样的原因其实跟早期的Sun公司宣传策略有关系,我们知道Java语言,其实是从C语言借鉴改进过来的,其中一个主要的优点就是不允许我们像C那样可以通过指针直接操作内存区域,为了这个事情,Sun公司替换了概念称Java的指针为引用,从而在Java语言的发展历程中导致了更大的困惑,持续演变至今。事实就是Sun公司命名错误,当然Java已经发展了很多年,Oracle接盘后也犯不上纠正命名的问题。结果就是懂的人永远都知道是怎么回事,不懂的人一直分不清这两个概念。
只有认清了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,所以并没有任何变化。
图示如下:
注意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语言,无论哪种方式,我们只要理解了其本质,就可以掌握的更好。
发表评论
-
记一次log4j不打印日志的踩坑记
2019-09-22 01:58 1458### 起因 前几天一个跑有java应用的生产集群(200多 ... -
在Java里面如何解决进退两难的jar包冲突问题?
2019-07-23 19:10 1142如上图所示: es api组件依赖guava18.0 ... -
如何轻松理解二叉树的深度遍历策略
2019-07-03 23:33 1017我们知道普通的线性数据结构如链表,数组等,遍历方式单一 ... -
为什么单线程Redis性能也很出色
2019-01-21 18:02 2133高性能的服务器,不一 ... -
如何将编程语言里面的字符串转成数字?
2019-01-11 23:23 1995将字符串转成数字在很 ... -
为什么Java里面String类是不可变的
2019-01-06 18:36 1589在Java里面String类型是不可变对象,这一点毫无疑问,那 ... -
关于Java里面volatile关键字的重排序
2019-01-04 18:49 985Java里面volatile关键字主 ... -
多个线程如何轮流打印ABC特定的次数?
2018-12-11 20:42 5930之前的一篇文章,我给 ... -
理解计数排序算法的原理和实现
2018-10-11 10:03 2047计数排序(Counting sort) ... -
理解Java7和8里面HashMap+ConcurrentHashMap的扩容策略
2018-09-06 11:31 3341### 前言 理解HashMap和Con ... -
关于Java里面多线程同步的一些知识
2018-07-18 09:45 1064# 关于Java里面多线程同步的一些知识 对于任何Java开 ... -
Java单例模式之双检锁深入思考
2018-07-08 12:25 3245# Java单例模式之双检锁 ... -
关于Java里面多线程同步的一些知识
2018-07-08 12:23 1084# 关于Java里面多线程同步的一些知识 对于任何Java开 ... -
重新认识同步与异步,阻塞和非阻塞的概念
2018-07-06 14:30 1426# 重新认识同步与异步 ... -
线程的基本知识总结
2018-06-27 16:27 1020### (一)创建线程的方式 (1)实现Runnable接口 ... -
Java里面volatile关键字修饰引用变量的陷阱
2018-06-25 11:42 1329# Java里面volatile关键字修饰引用变量的陷阱 如 ... -
关于Java里面的字符串拼接,你了解多少?
2018-06-25 11:28 1317# 关于Java里面的字符串 ... -
深入理解Java内存模型的语义
2018-06-25 11:39 692### 前言 Java内存模型( ... -
如何证明Java多线程中的成员变量数据是互不可见的
2018-06-21 10:09 1455前面的几篇文章主要介绍了Java的内存模型,进程和线程的定义, ... -
给Java字节码加上”翅膀“的JIT编译器
2018-06-20 10:12 996# 给Java字节码加上”翅 ...
相关推荐
我聊2013我聊2013java通用聊天版
2. 聊聊并发(二)Java SE1.6中的Synchronized 3. 聊聊并发(三)Java线程池的分析和使用 4. 聊聊并发(四)深入分析ConcurrentHashMap 5. 聊聊并发(五)原子操作的实现原理 6. 聊聊并发(六)...
聊聊Java平台上的非Java语言共3页.pdf.zip
基于java socket 做的简单的聊天系统、简单实现了单聊群聊功能、、
java单聊群聊案例,课程代码,适合新手学习,基础知识的掌握
基于java socket编程、简单实现了单聊群聊功能、、
java写的聊天程序,可以公聊私聊,截图,发图片……
聊聊并发(3)Java线程池的分析和使用Java开发Java经验技巧共7页.pdf.zip
第一次使用这个博客,诸多疑惑,同时Java中的小白,聊的问题较为浅显,大佬勿怪.
所有IM聊项目的客户端和服务器端的java源代码
聊聊并发(5)原子操作的实现原理Java开发Java经验技巧共8页.pdf.zip
Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序...Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊
用UDP实现两人互聊。 udp进行通信 socket()
Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序...Java聊天室程序(java)Java聊天室程序(java)Java聊天室程序(java)Java聊
疯聊 - 基于Java Swing编写的即时通讯应用 (in XMETC)
来源k6k4 答案参考:java常见面试题 1、面向对象的特点有哪些? 2、接口和抽象类有什么联系和区别? 3、重载和重写有什么区别? 4、java有哪些基本数据类型? 5、数组有没有length()方法?...9、什么是值传递和引用传递?
java 教程 离线文档_廖雪峰_pdf
JAVA公聊 私聊 现在在线用户加头像 发送文件等
java swing语音聊天室,文字的有单聊,群聊,强制下线等,还可以登录,没有数据库,直接下载运行,jdk1.8,ecilpse和idea都可以运行
群聊和单聊, 项目是 Java 语言开发,开发工具是 MyEclipse ,导入后 直接可以启动, 需要tomcat7 或者 8,里面用到了三个包! 希望 下载 用到的 同志们,给好评,谢谢!如果有问题,可以提出,里面的注释写的很全.....