当前位置 : 主页 > 编程语言 > 其它开发 >

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

来源:互联网 收集:自由互联 发布时间:2022-05-14
参数传递 Java 中的参数传递分为 “值传递”“引用传递” 如果你学过 C/C++ 应该很好理解,就是所谓的 "值传递" 和 "指针传递" 值传递 在 Java 中,"值传递" 就是传递真实值的一个副本,
参数传递

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 基础太拉了,哈哈
然后现在发现其实原理就这么简单,插入后把 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
很多同学应该也是这个表情

“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 关键字,所以会生成一个新的对象,但是 new String("ab") 中的 "ab" 在常量池中存在,所以也复用了常量池中的 "ab",但是 s2 和 s3 并不是同一个对象,因为你使用了 new 关键字,会重新生成一个对象
第六行:池里没有
"cd" ,创建 "cd" 扔到池子里,s3 是 "ab" ,用加号连接起来就是 "abcd",池里没有 "abcd",创建 "abcd" 扔到池里
第七行:
s5 = "abcd",因为池里有 "abcd",所以直接复用就是,不用重新创建
所以一共生成了 5 个对象,分别是:
"a","ab","ab","cd","abcd"**
至于 Java 为什么要这样设计,大家感兴趣可以去探究探究
据我所知,Java 里用得最多的数据类型就是 String,如果不停的创建 String 对象,对内存来说应该是一笔很大的开销,使用常量池可以对其进行复用,算是对 String 的优化
可能不太准确,hhh,也欢迎大佬在下面评论
文笔不是很专业,但还是希望你没看一篇博客都能吸收到新的知识,认真沉淀~

上一篇:Spring注解开发_Spring容器创建概述
下一篇:没有了
网友评论