String的按值传递,java传参都是传值

简介:

java中对象作为参数传递给一个方法,到底是值传递,还是引用传递?

String和int参数传递是按值传递还是引用传递?

一道面试题目,String的传递:

1
2
3
4
5
6
7
8
9
public  String change(String s){
      s =  "222" ;
      return  s; 
}
public  static  void  main(Stirng[] args){
     String s =  "111"
     change(s);
     sout(s);
}

我看到题目愣了一下,本来不假思考的结果是111,但仔细想,String是对象类型的,对象传递的是地址,那么地址传递到方法里面后,将指向修改成222,那么结果应该是222才对。实际恰恰相反。

Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。 

java传递参数都是值,如果是对象的话,就是将引用的值复制一份给方法当参数。如果是根据引用把堆里的对象修改了,那么对象真被修改了,不过不是被创建赋值给的那个引用修改的,是方法里的一个复制的引用副本给修改的。换句话说,施瓦星格的媳被施瓦星格的克隆人亲了下。

用实例去理解,其实这个理解也就是根据jdk的结果告诉我自己记住规则是这样的,以后要记住。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   public  String change(String s,  int  i, StringBuffer sb, Person p){
         s= "123" ;
         i= 3 ;
         sb.append( "woshi" );
         p.setAge( 100 );
         sb =  new  StringBuffer( "sbsb" );
         p =  new  Person( "bb" , 44 );
         return  s;
     }
 
     @Test
     public  void  testChange(){
         StringBuffer sb =  new  StringBuffer( "buff" );
         String s =  "aaa" ;
         int  i =  1 ;
         Person p =  new  Person( "aa" , 12 );
         i= 2 ;
         change(s,i,sb,p);
//        s="222";
         System.out.println(s);
         System.out.println(i);
         System.out.println(sb.toString());
         System.out.println(p);
     }

这里一共测试了String,int,一个对象StringBuffer,一个对象people。让我们来仔细看看这些传递都发生了什么。我想有很大一部分人都猜不出打印结果。

我们来一个个分析。

首先是String。

1
String s =  "aaa" ;

这里,jvm创建一个变量引用s,在堆中创建一个对象aaa,将aaa放进常量池。s指向aaa。

然后就到了change方法里。这里这样理解:将s引用的一个拷贝传给方法change。这样change有一个变量s,这个s也是指向aaa的。那么我们来通过debug来看后来发生了什么。

1.s指向aaa的时候:

2.s运行到change方法里的时候

然后看s再次赋值的时候:

然后我们运行结束change方法后到主方法里:

到这里s就结束了。那么如果我们按照传递的是s这个变量的引用,即String s="aaa"中这个s本身,那么,s这个本身是个变量,s指向aaa,在方法change里s又指向了123,回到主方法后s变量的指向被改变了?错!显然s仍旧是aaa,那么只能这样理解:s传递到方法里的时候,复制了s指向的地址给change,change里的s是另一个s,s指向aaa(@718),然后在change中s又指向了123(@731),由于String是不可变类(final and Immutable),这里只是把副本s的指向修改成731,原地址718里的对象没有发生改变因为String不可变。那么,回到主方法的时候,s变量本身没有任何改变,s仍旧指向地址718,718的内容是aaa。所以最终打印aaa。

然后是StringBuffer

int是基本类型,所以int只是将值复制一份给别的方法用,这个大家都知道,就不去测试了。现在看StringBuffer发生的改变。

1.初始化:

2.到change方法中:

3.发生append

4.指向新对象

这里就要说一下了,副本指向了新对象。就好比,施瓦星格的克隆人找了另一个女的当老婆,而真正的施瓦星格老婆没有变。

5.回到主方法:

 

到这里,StringBuffer就结束了。我们必须知道,虽然我们没有去研究源码是怎样实现的,change方法得到是一个sb的副本,只不过这个副本指向708,在change里对708的对象追加,708的对象就真的改变了。然后又把sb副本指向新地址737。这只是把副本指向的地址修改了,如果你在这里打印sb.toString(),打印的就是737里的内容。当跳出change,回到主方法的时候,原sb仍旧还是指向708的,最终就是打印708的结果。和String不同的是,StringBuffer的结果发生了变量,因为StringBuffer是可变的,可以append。而String是不可变的,在change中s=123就是发生两个行为,一个是查找常量池中是否有123,如果没有就在堆中创建123,一个是将s指向123.也就是说这时候是创建了一个新的String对象,而不是把原来的String对象s内容修改。这样,回到主方法的时候,s仍旧是aaa。

同理,看自己创建的对象people

 

1.初始化:

2.p传递到change里的时候

3.p副本设置age

4.p副本重新赋值

 这里仍旧要说一下,p副本修改了自己指向,并不影响主方法里的p的指向。主方法里的p的指向没有发生变化,依旧应该还是720.

5.回到主方法

总结:

通过上面对String,StringBuffer,People的研究,应该明白一个道理,重要的话说三遍,重要的规则我都演示了三遍。如果跟着步骤一步步走的,肯定牢记住了:

java所有的参数传递都是传递的副本,变量所代表的值的副本!java所有的参数传递都是传递的副本,变量所代表的值的副本!java所有的参数传递都是传递的副本,变量所代表的值的副本!

这里必须记住的就是副本概念。在方法里,运行的时候到这里的线程都会把传过来的参数拷贝副本带自己的工作区中,在工作区中对这个副本的值发生一些改变。最终改变的是副本,如果通过副本的指向修改了指向中的内容,那么那个指向的地址里的内容确实改变了。如果修改了副本的指向,即给副本重新赋值,那么关原来的变量何事?元变量仍旧指向最初的地址。

那么,String传递过去的是副本,修改了副本的指向,打印元string是不会改变的,因为副本没有能力修改final的String类。

 

本文转自Ryan.Miao博客园博客,原文链接:http://www.cnblogs.com/woshimrf/p/java-transfer-value.html,如需转载请自行联系原作者

相关文章
|
2月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
201 14
|
6月前
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
143 11
|
6月前
|
Java
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、"+"操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
148 9
|
9月前
|
JSON 前端开发 Java
【Bug合集】——Java大小写引起传参失败,获取值为null的解决方案
类中成员变量命名问题引起传送json字符串,但是变量为null的情况做出解释,@Data注解(Spring自动生成的get和set方法)和@JsonProperty
|
9月前
|
存储 JavaScript Java
Java 中的 String Pool 简介
本文介绍了 Java 中 String 对象及其存储机制 String Pool 的基本概念,包括字符串引用、构造方法中的内存分配、字符串文字与对象的区别、手工引用、垃圾清理、性能优化,以及 Java 9 中的压缩字符串特性。文章详细解析了 String 对象的初始化、内存使用及优化方法,帮助开发者更好地理解和使用 Java 中的字符串。
157 2
Java 中的 String Pool 简介
|
9月前
|
存储 Java
Java 11 的String是如何优化存储的?
本文介绍了Java中字符串存储优化的原理和实现。通过判断字符串是否全为拉丁字符,使用`byte`代替`char`存储,以节省空间。具体实现涉及`compress`和`toBytes`方法,前者用于尝试压缩字符串,后者则按常规方式存储。代码示例展示了如何根据配置决定使用哪种存储方式。
184 1
|
9月前
|
缓存 安全 Java
java 为什么 String 在 java 中是不可变的?
本文探讨了Java中String为何设计为不可变类型,从字符串池的高效利用、哈希码缓存、支持其他对象的安全使用、增强安全性以及线程安全等方面阐述了不可变性的优势。文中还通过具体代码示例解释了这些优点的实际应用。
203 1
java 为什么 String 在 java 中是不可变的?
|
10月前
|
Java
在Java中如何将基本数据类型转换为String
在Java中,可使用多种方法将基本数据类型(如int、char等)转换为String:1. 使用String.valueOf()方法;2. 利用+运算符与空字符串连接;3. 对于数字类型,也可使用Integer.toString()等特定类型的方法。这些方法简单高效,适用于不同场景。
464 7
|
10月前
|
JSON Java 关系型数据库
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
在Java中,使用mybatis-plus更新实体类对象到mysql,其中一个字段对应数据库中json数据类型,更新时报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.
1082 4
Java更新数据库报错:Data truncation: Cannot create a JSON value from a string with CHARACTER SET 'binary'.