- 浏览: 167592 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
mengyue0477:
最后还不忘吐槽一下 哈哈 太逗了
spring容器的懒加载lazy-init设置 -
comet0515:
不是maven工程能不能用checkstyle插件?job是一 ...
jenkins 配置checkstyle -
xfxlch:
http://docs.spring.io/spring-bo ...
NoteBook -
With_Me_Forever:
太浅了。我最近也在看着方面的资料,一起进步把。
互联网金融定义 -
xfxlch:
在http://r.m.baidu.com/6pmxgig
NoteBook
问题:
最近在看Martin Fowler的《重构》一书,书中在讲临时变量的时候提到,编程的时候尽量不要去改变入参的值,因为这样的当时开发者来说是比较能理解的,但是对于后续维护者来说,这个就会比较头大。因为有时候我们根本就搞不明白为什么进入的时候是这样的,出来的为什么不是我要的值呢。因此, Martin Fowler建议如果要对入参做改变,可以定义一个返回值,然后把这个返回值重新复制给一个新的变量。
e.g.
虽然,代码多了一些,但是对于后续的维护是有大大的好处的,毕竟程序是写给人看的。
为了不改变入参的值,Martin Fowler还建议要给形参加上final修饰符,这样这个变量就不会被强制复制了。
说到这里的时候,提到了java开发经常会出错的一个点就是:java 的值传递问题。
名词解释:
值传递:java程序中,调用者调用被调用者的时候,通常都是把调用者的参数的值“拷贝”一份来进行操作的。Java里面所有的传递都是按值传递
简单说就是上面的main方法在调用changeUser方法的时候,会进行user=user1的赋值操作。也就是说,实际上我们在changeUser里做的任何改动都是对拷贝出来的副本进行的改动,不会对原来的值user1造成任何影响。那么上面的代码为什么user的值被改变了呢? 我们这里的原因有两个,一个是我们在main方法里对user重新进行了赋值。 第二个我想说的是,这里即使没有赋值也就是代码变成这样:
output也还是这样的
下面就是解释一下原因:
例子1:基本数据类型:
也就是说我们期望的想把5,放大3倍的效果并没有出来。为什么,因为在triple方法体内,我们是对另外一个副本进行了操作,但是在方法体外,x还是原来的那个值,除非,你对它重新赋值。
例如这样:
例子2:引用类型(对象,数组):
对象:
你会发现username被修改了,你很知道,为什么,不是说只对副本进行操作么,这里的值为什么会被修改了呢。
那就让我来解释一下吧。 我们都知道对象是引用类型,那么引用类型,它的值是什么呢,它总有一个值吧,不然这个东西不可能凭空存在的啊。对的,引用对象它说存的值是一个对象实例在堆内存中的地址,也就是类似:0x0000F000的值,它也是一个值,只不过它指向了另一个内存区域。这就是为什么说java中只有按值传递的原因了。
我们这个例子中,我们调用rename方法,这时我们把user的引用地址copy了一份,但是,当我们执行.操作的时候,我们是去修改的这个引用所指向的真实内存空间里的值。 就好比我在淘宝上买了个电视,我把我家的地址copy一份给快递员,快递员拿到我家的地址之后,就往我家里送电视,等电视送到了,我家里就多了一个电视的。而不是别人的家里多了电视。
同样的,如果我改动代码如下,这里先把final去掉:
output就变成这样了。
原因就是,user这个地址指向了其他人的地址,也就是我给了一个其他人的地址给快递员,东西并没有送到我家里来,那么我家里还是原来的样子,东西不会多也不会少。
数组也是一样的道理, 如下:
output:
这个值会被改变就等同于,user的真实内存内容被修改了。
例子3:final修饰
当用final修饰时,这时的形参是不能被改变的,也就是这里那个注释掉的代码是不能被执行的,原因就是地址是不能再被重新赋值的,而setter能执行就说明,当我们执行setter的时候,并没有对这个地址值本身进行操作,而是对这个地址所指向的堆内存进行了操作。
不知道各位有没有跟理解一点呢 。 。 。
----EOF----
最近在看Martin Fowler的《重构》一书,书中在讲临时变量的时候提到,编程的时候尽量不要去改变入参的值,因为这样的当时开发者来说是比较能理解的,但是对于后续维护者来说,这个就会比较头大。因为有时候我们根本就搞不明白为什么进入的时候是这样的,出来的为什么不是我要的值呢。因此, Martin Fowler建议如果要对入参做改变,可以定义一个返回值,然后把这个返回值重新复制给一个新的变量。
e.g.
public class Demo { static User user1 = new User("clu", 111); public static void main(String args[]){ User user2 = changeUser(user1); System.out.println(user2.getName()); } public static User changeUser(User user) { User userchanged = user; userchanged.setName("jack"); return userchanged; } }
output: jack
虽然,代码多了一些,但是对于后续的维护是有大大的好处的,毕竟程序是写给人看的。
为了不改变入参的值,Martin Fowler还建议要给形参加上final修饰符,这样这个变量就不会被强制复制了。
说到这里的时候,提到了java开发经常会出错的一个点就是:java 的值传递问题。
名词解释:
值传递:java程序中,调用者调用被调用者的时候,通常都是把调用者的参数的值“拷贝”一份来进行操作的。Java里面所有的传递都是按值传递
简单说就是上面的main方法在调用changeUser方法的时候,会进行user=user1的赋值操作。也就是说,实际上我们在changeUser里做的任何改动都是对拷贝出来的副本进行的改动,不会对原来的值user1造成任何影响。那么上面的代码为什么user的值被改变了呢? 我们这里的原因有两个,一个是我们在main方法里对user重新进行了赋值。 第二个我想说的是,这里即使没有赋值也就是代码变成这样:
public class Demo { static User user1 = new User("clu", 111); public static void main(String args[]){ // User user2 = changeUser(user1); System.out.println(user1.getName()); } public static User changeUser(User user) { User userchanged = user; userchanged.setName("jack"); return userchanged; } }
output也还是这样的
output: jack
下面就是解释一下原因:
例子1:基本数据类型:
//code from refactorying public static void main(String[] args){ int x = 5; triple(x); System.out.println("x after triple: " + x); } /** * @param x */ private static void triple(int x) { // TODO Auto-generated method stub x = x *3; System.out.println("x in triple : " + x); }
output: x in triple : 15 x after triple: 5
也就是说我们期望的想把5,放大3倍的效果并没有出来。为什么,因为在triple方法体内,我们是对另外一个副本进行了操作,但是在方法体外,x还是原来的那个值,除非,你对它重新赋值。
例如这样:
public static void main(String[] args){ int x = 5; x= triple(x); System.out.println("x after triple: " + x); } /** * @param x */ private static int triple(int x) { // TODO Auto-generated method stub x = x *3; System.out.println("x in triple : " + x); return x; }
例子2:引用类型(对象,数组):
对象:
public static void main(String[] args){ User user = new User("clu" , 123); rename(user); System.out.println(" user after User name: " + user.getName()); } /** * @param user */ private static void rename(final User user) { // TODO Auto-generated method stub if(user != null) { user.setName("xxxxxx"); } System.out.println(" user in User name: " + user.getName()); }
output: user in User name: xxxxxx user after User name: xxxxxx
你会发现username被修改了,你很知道,为什么,不是说只对副本进行操作么,这里的值为什么会被修改了呢。
那就让我来解释一下吧。 我们都知道对象是引用类型,那么引用类型,它的值是什么呢,它总有一个值吧,不然这个东西不可能凭空存在的啊。对的,引用对象它说存的值是一个对象实例在堆内存中的地址,也就是类似:0x0000F000的值,它也是一个值,只不过它指向了另一个内存区域。这就是为什么说java中只有按值传递的原因了。
我们这个例子中,我们调用rename方法,这时我们把user的引用地址copy了一份,但是,当我们执行.操作的时候,我们是去修改的这个引用所指向的真实内存空间里的值。 就好比我在淘宝上买了个电视,我把我家的地址copy一份给快递员,快递员拿到我家的地址之后,就往我家里送电视,等电视送到了,我家里就多了一个电视的。而不是别人的家里多了电视。
同样的,如果我改动代码如下,这里先把final去掉:
public static void main(String[] args){ User user = new User("clu" , 123); rename(user); System.out.println(" user after User name: " + user.getName()); } /** * @param user */ private static void rename( User user) { // TODO Auto-generated method stub // if(user != null) { // user.setName("xxxxxx"); // } user = new User("jack", 333); System.out.println(" user in User name: " + user.getName()); }
output就变成这样了。
output: user in User name: jack user after User name: clu
原因就是,user这个地址指向了其他人的地址,也就是我给了一个其他人的地址给快递员,东西并没有送到我家里来,那么我家里还是原来的样子,东西不会多也不会少。
数组也是一样的道理, 如下:
public static void main(String[] args) { int[] count = { 1, 2, 3, 4, 5 }; change(count); System.out.println(" user after User name: " + count[0]); } /** * @param count */ private static void change(int[] count) { // TODO Auto-generated method stub count[0] = 6; }
output:
user after User name: 6
这个值会被改变就等同于,user的真实内存内容被修改了。
例子3:final修饰
public static void main(String[] args) { User user = new User("clu", 1111); rename(user); } /** * @param user */ private static void rename(final User user) { if(user != null) { user.setName("xxxxxx"); } // user = new User("jack", 333); System.out.println(" user in User name: " + user.getName()); }
当用final修饰时,这时的形参是不能被改变的,也就是这里那个注释掉的代码是不能被执行的,原因就是地址是不能再被重新赋值的,而setter能执行就说明,当我们执行setter的时候,并没有对这个地址值本身进行操作,而是对这个地址所指向的堆内存进行了操作。
不知道各位有没有跟理解一点呢 。 。 。
----EOF----
发表评论
-
eclipse project .setting
2016-08-24 11:30 606有一个文件在.setting的目录下面: 文件名:org.ec ... -
jar命令工具
2016-01-23 20:29 690背景: 今天介绍两个jar的命令工具,对我个人来说,还是比较好 ... -
Mac OS 安装java 开发环境
2016-01-03 13:00 1775我的机器是: 1. jdk1.7下载安装 jdk7:h ... -
记一次Java Rest Service Hang住的经历
2015-12-28 20:08 2704背景: 在前后端分开开发的过程中,前段的页面要调用后台Java ... -
一个String字符串的问题2
2015-12-14 14:22 612背景: 今天遇到个问题: String str = & ... -
我读重构这本书
2015-12-01 22:30 0第一点:关注变量命名。好的变量名能让读者更快更好的理解程序到底 ... -
系统log的编写
2015-11-16 09:41 0log很关键,一定要记录好对追踪问题有帮助的日志。 -
CAS 授权问题
2015-11-16 09:40 0CAS 授权问题 -
Caused by: java.lang.UnsupportedOperationException 解决方案
2015-10-21 22:09 4085背景: 今天在跑一个UnitTest,跑的过程中想在list ... -
LogBack 框架里log-access的使用
2015-10-18 16:05 7109背景: 想通过配置log-access来实现直接使用浏览器来访 ... -
how to fix "org.hamcrest.CoreMatchers.containsString cannot be resolved"
2015-09-23 23:12 3031背景: 山姆大叔比较喜欢用一些新的技术和新的特性,最近在jun ... -
java 打印金字塔
2015-08-24 00:53 787背景: 一个同事,面试应聘者喜欢用学校里学过的一个书本或者课后 ... -
纯jsp servlet 实现 upload file功能
2015-08-17 20:35 747本篇主要是简单的实现jsp servlet 的上传文件的功能, ... -
dos 命令窗口執行java 命令,帶包路徑
2015-07-26 12:16 535記錄一下,是為了自己下次再run java命令的時候,可以很快 ... -
tomcat 远程调试
2015-07-23 18:41 492背景: 我们在项目开发过程中,很多时候会遇到这样的情况:项目在 ... -
使用android studio 来开发hello world app的时候,遇到的一些坑。
2015-07-13 01:38 2165使用android studio 来开发h ... -
ThreadLocal
2015-05-11 07:22 429Java线程:深入ThreadLocal ThreadLo ... -
Hibernate Interceptor(拦截器)
2015-04-28 20:30 2429需求: 对所有操作数据库的事件,添加audit log, 此l ... -
eclipse 加入tomcat 包
2015-03-24 01:09 433... -
Transforming XML with XSLT
2014-10-11 19:14 528public static void catalog() ...
相关推荐
### Java中的按值传递与按引用传递详解 #### 一、引言 在Java编程语言中,关于参数传递的方式一直存在两种观点:一种认为Java仅支持按值传递,另一种则指出Java同时支持按值传递和按引用传递。实际上,这两种观点...
在Java中,所有的参数传递都是按值传递,但这并不意味着对象变量也是如此。这是因为Java中对象的引用是按值传递的,而对象本身则是按引用访问的。以下是对这个概念的详细解释。 1. **按值传递**: 在Java中,当...
"Java按值传递还是按引用传递" Java是一种面向对象的编程语言,在Java中,对于方法的参数传递有两种方式:按值传递和按引用传递。这两种方式都有其特点和应用场景,本文将对这两种方式进行详细的介绍和分析。 一、...
Java中传递对象时传递的并不是对象中的内容, 而是对象的地址。
Java编程语言中的参数传递主要有两种方式:按值传递(pass by value)和按引用传递(pass by reference),尽管Java官方并不支持真正的按引用传递,但其行为类似于按引用传递。这两种传递方式在面试和笔试中经常出现...
在Java中,基本数据类型(如int、char、float等)就是按值传递的。例如,如果你有一个整型变量`int a = 5;`,并将其作为参数传递给一个函数,函数内部对这个参数的任何修改都不会影响到原始变量`a`的值,因为传递的...
在Java编程语言中,了解值传递和引用传递的概念至关重要,因为它们直接影响到函数参数的处理方式。下面将详细探讨这两个概念及其区别。 首先,我们来理解什么是值传递。在Java中,基本数据类型(如int、double、...
标题“Java是值传递,传对象引用也是通过值”揭示了Java中参数传递的核心概念。Java总是以值传递方式进行,这意味着当你将一个变量作为参数传递给方法时,传递的是该变量所存储值的一个副本。然而,对于对象类型的...
Java编程语言中有两种参数传递方式:按值传递和按引用传递。理解这两种方式对于编写高效、无误的代码至关重要。 1. **按值传递(Pass by Value)** - Java中的基本类型(如int, double, char等)是按值传递的。这...
JAVA 参数传递方式实例浅析【按值传递与引用传递区别】 JAVA 参数传递方式实例浅析【按值传递与引用传递区别】是 JAVA 编程语言中的一种基本概念,对于 JAVA 开发者来说是非常重要的。本文将通过实例形式分析 JAVA ...
在本教程中,我们将探讨如何在Java和HTML的结合中实现值传递,以便在网页上绘制圆圈,这对于初学者来说是一个很好的实践项目。 首先,我们要理解Java和HTML之间的交互。通常,这种交互是通过Servlet或JSP(Java...
Java 面向对象值传递和引用传递 Java 面向对象编程中,参数传递是非常重要的一个概念。参数传递有两种方式:值传递和引用传递。了解这两种方式的区别是非常重要的,因为它们对程序的执行结果产生了很大的影响。 值...
我们先看一下值传递和引用传递的概念和区别 值传递:是指在调用函数时将实际参数复制一份传递到函数中,...我们通过例子理解一下Java的值传递: public static void main(String[] args) { int a = 10; int b = 20;
Java中的按值传递和按引用传递的代码详解 本文通过实例代码详细解释了Java中的按值传递和按引用传递的相关知识。通过实验,我们可以了解Java中基本类型和引用类型的传递机制。 按值传递 在Java中,基本类型的变量...
详解java的值传递、地址传递、引用传递 java是一种面向对象的编程语言,它的参数传递机制是值传递的,而不是地址传递或引用传递。很多开发者对java的值传递和地址传递存在误解,认为java中的基本数据类型是值传递,...
在Java中,参数传递有两种方式:值传递和引用传递。这个“Java 值传递Visio资源”包含了几个Visio图形文件,帮助我们直观地理解这两种传递方式。 1. **值传递**: 当方法调用时,对于基本类型(如int, double, char...
java学习java语言的值传递和引用传递