String类的不可变性

4 篇文章 0 订阅

Java中数据类型的传递分为值传递和引用传递,一般来说,基本数据的传递为值传递,引用数据类型的传递为引用传递。

1:值传递

实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

2:引用传递

方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。

那么,我们知道String类是一个引用数据类型,但是,Java中对String的声明是一旦定义就不可改变,那么为什么我们在使用String类型的对象作为参数传递时,却不会改变原来的String对象呢?

    首先,我们看以下例子:

public class StringTest2 {

	public static void main(String[] args) {
		String str1="abc123";
		System.out.println(str1);-----------------------abc123
		System.out.println(changeString(str1));---------abc123ABC
		
	}
	public static String changeString(String str){
		return str+"ABC";
	}
}

初次看上去,可能会以为str1的值已经改变了,实际上是这样吗,我们继续下一个例子:

public class StringTest2 {

	public static void main(String[] args) {
		String str1="abc123";
		System.out.println(str1);-------------------------------abc123
		System.out.println(changeString(str1));-----------------abc123ABC
		System.out.println(str1);-------------------------------abc123
		System.out.println(str1.equals(changeString(str1)));----false
		
	}
	public static String changeString(String str){
		String str1=str+"ABC";
		return str1;
	}
}

可以发现,其实str1的值并没有改变,那么String作为应用数据类型,为什么它的值在这里没有改变呢?

这里有一个概念是:每一个字符串都是一个String类的匿名对象

接下来,我们先看一下String的内存分配:

由此我们发现,当执行str+"ABC"时,其实并不是对str这个对象进行原地操作,而是重新声明了一个String对象,其在堆内存中重新开辟了一个空间来保存值。由上图也引发了另一个问题,那就是String的两种格式化方式的区别:

public class StringTest2 {

	public static void main(String[] args) {
		String str1="abc123";
		String str2=new String("abc123");
		
		System.out.println(str1==str2);==================false
		System.out.println(str1.equals(str2));===========true
	}

}

为什么同样是"abc123"的两个字符串对象,他们的比较结果却出现了差异,以及这两种比较方式的区别是什么?

再补充一点:匿名对象就是已经开辟了堆内存空间的并可以直接使用的对象!

已经很明确了,由此可见使用"="声明的对象"abc123"并不是和通过new方式声明的对象是同一个对象!

那么,“==”和equals方法的为什么结果不同呢?

“==”:是完全比较,在比较基本数据类型时,其比较的是“值”,在比较应用数据类型时,其比较的是“地址值”,所以不同的对象其地址值不同,因此上面结果为false;

“equals”:这个方法我们其实是使用Objec对象中的equals方法的,因为Object是所有Java类的父类,起初它的作用也是和“==”一样的,但是String类,以及Integer,Date类覆写了这个方法,在String类中它起得作用就是比较“值”是否相等。所以看起来一样的String对象,使用“equals”比较的结果就是“true”。

从两种方法的比较可以看出,如果使用"=="比较值为“true”,那么使用“equals”比较的值肯定也为“true”

返回到上一个问题,String类的这两种声明方式哪个更好的?

很显然,对于相同的值我们并不需要每次使用就声明一次,这样会产生不必要的内存垃圾,所以使用“=”直接赋值是更好的操作。这种模式在Java中称为“共享设计”模式。这里需要在引入一个概念就是“字符串常量池”:

字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。

所以,我们所看见的String对象的改变,其实是通过内存地址的“断开——连接”变化完成的,而字符串本省并没有任何改变。

实际上,String类的常用方法中,我们也可以发现,String类并不提供能够原地改变对象的方法,当返回值为String的时候,我们就能够明白,这个返回的其实是另一个已经声明的对象,而不再是之前传入的参数了,

由此,我们在开发中就应该避免对String类的大量修改操作。譬如:

public class StringTest2 {

	public static void main(String[] args) {
		String str1="abc123";
		for (int i = 0; i < 100; i++) {
			str1+=i;
		}
	}
}

这种操作可以使用StringBuffer类来完成。

那么,由此引发了另一个问题:为什么String类要设计成不可变模式呢?

对于这个问题,我个人理解使因为String是程序中使用最普遍的类型,处于安全性考虑,不可变是稳妥的。由此带来了内存的节省等其他好处。



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值