浅谈Java-String到底是值传递还是引用传递?


参数传递

Java 中的参数传递分为 “值传递”“引用传递”
如果你学过 C/C++应该很好理解,就是所谓的 "值传递""指针传递"

值传递

在 Java 中,"值传递" 就是传递真实值的一个副本,其实就是重新生成了一个值,新的值怎么改变并不影响原来的值,举个例子:

public class Test { 	 	public static void main(String[] args) { 		Test t1 = new Test(); 		int a = 1; 		t1.paramTest(1); 		System.out.println("main: " + a); 		 	} 	 	private void intParamTest(int a) { 		a++; 		System.out.println(a); 	}  } 

输出结果:
2
main: 1
我们可以看到,虽然我们去调用了方法让 a 的值自增 1 ,但是 main 方法中的 a 还是 1
实践出真知:我们可以得出一个结论,基本数据类型是值传递(传递的是值的一个克隆)

引用传递

引用传递 顾名思义传递的是参数的引用,也就是参数的地址,在 Java 中,对象作为参数传递时,传递的就是对象的引用地址
所以当我们对 对象进行操作的时候,实际上我们操作的是真实的对象,原对象也会随之改变,还是来实践出真知:

public class Test { 	 	public static void main(String[] args) { 		Test t1 = new Test(); 		Person p = new Person("小冯同学",18); 		System.out.println("方法调用前:" + p.name); 		t1.personTest(p); 		System.out.println("方法调用后:" + p.name); 		 	} 	 	private void personTest(Person p) { 		p.name = "张三同学"; 	}  } class Person{ 	public String name; 	public int age; 	public Person(String name,int age) { 		this.name = name; 		this.age = age; 	} } 

输出结果:
方法调用前:小冯同学
方法调用后:张三同学
看到这里,我们可以得出一个结论:对象作为参数的时候,传递的是对象的引用地址。

总结

根据以上两个结论,我们总结一下:

  • 当参数类型是基本数据类型时,传递是的一个拷贝值,拷贝值是生是死不影响原来的值
  • 当参数类型是一个对象时,传递的是对象的引用地址,操作的是同一个对象

分享一个小故事,大概是两年前刚开始接触 SSM框架的时候,当时有一个需求就是:插入一个 user,然后返回插入成功和 user的基本信息(包括 userid),然后我们呢当时数据库的 user表的 id自增的,插入的 user是没有 id的,id是插入后 mysql自增主键生成的,当时借助了 mybatis的主键回添功能,大概长这样子:

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">     insert into t_user (u_name,u_age) values (#{name},#{age}); </insert> 

然后之前插入的那条 user记录就有 id了,当时年少气盛的我还不懂什么值传递引用传递,当时的表情是这样的
浅谈Java-String到底是值传递还是引用传递?
属实是 Java 基础太拉了,哈哈
然后现在发现其实原理就这么简单,插入后把 id 查出来,直接塞到你之前插入的那个 user 对象中 o 了

String

相信大家在面试场上,面试官经常会问你一些关于 String 的问题
然后你发现你之前背的结论不管用了(基本数据类型值传递,对象引用传递
还是实践出真知:

public class Test { 	 	public static void main(String[] args) { 		Test t1 = new Test(); 		String s = "hello"; 		t1.append(s); 		System.out.println("main: " + s); 		 	} 	 	private void append(String s) { 		s += " world"; 		System.out.println("method: " + s); 	}  } 

输出结果:
method: hello world
main: hello
很多同学应该也是这个表情
浅谈Java-String到底是值传递还是引用传递?
“String 是对象啊,传递的不是地址吗,操作的不是真实的对象吗,为什么没有改变啊”
同学们呼声很大啊
你可以把 String 归类到基本数据类型中去,我这样说大家肯定不会买账,这还要从 String 的关键字 final 说起
大家都知道被 final 修饰的都是常量,常量有什么特点?当然是不能改变啊
所以我们对 String 进行改变的时候,编译器在底层是为我们重新生成了一个 String 对象,来看一道经典面试题:

	public static void main(String[] args) { 		String s1 = "a"; 		String s2 = s1; 		s2 = "ab"; 		String s3 = new String("ab"); 		String s4 = s3 + "cd"; 		String s5 = "abcd"; 	} 

请问这段程序一共生成了多少个对象?
有没有猜到 5 个的,正确答案就是 5 个。
池化技术了解过吧?线程池,对象池,人才池...
String 底层用到的就是 常量池
第二行:s1 = "a" ,生成常量 a 放入池中,然后 s1 指定这个常量的地址
第三行:s2 = s1 ,就是生成一个 s2 的变量指向 s1 的地址。
第四行:s2 = "ab",是把 "a" 变成的 "ab" 么?当然不是,人家都是常量了,不能被改变,那怎么办?当然是重新生成一个常量 "ab",放入池中,然后 s2 指向 "ab" 的地址
第五行: s3 = new String("ab"),使用了 new 关键字,所以会生成一个新的常量 "ab",(当然官方也不建议我们这样做,这样就不能复用池中的 "ab" 了)
第六行:池里没有"cd" ,创建 "cd" 扔到池子里,s3 是 "ab" ,用加号连接起来就是 "abcd",池里没有 "abcd",创建 "abcd" 扔到池里
第七行:
s5 = "abcd"
,因为池里有 "abcd",所以直接复用就是,不用重新创建
所以一共生成了 5 个对象,分别是:"a","ab","ab","cd","abcd"
至于 Java 为什么要这样设计,大家感兴趣可以去探究探究
据我所知,Java 里用得最多的数据类型就是 String,如果不停的创建 String 对象,对内存来说应该是一笔很大的开销,使用常量池可以对其进行复用,算是对 String 的优化
可能不太准确,hhh,也欢迎大佬在下面评论
文笔不是很专业,但还是希望你没看一篇博客都能吸收到新的知识,认真沉淀~

发表评论

相关文章