Java String相关问题

本文深入探讨了Java String的不可变性,解释了为何设计为不可变以及其带来的好处,如线程安全和计算效率。详细阐述了String的intern()方法,包括字符串池的概念和方法作用。同时,文章还分析了new String("hello")创建对象的细节,讨论了不同字符串拼接方式对内存的影响,强调在循环和大文件处理中避免使用String拼接以防止内存抖动问题。最后,探讨了Java String的长度限制及其在内存中的存储方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、String 的不可变特性

String 可能是 Java 中最特殊的一个数据类型。

  • 和基本数据类型的封装类型一样,具备不可变性
  • 声明方式像基本数据类型,但其实是引用类型
  • 作为引用类型,又不一定直接放在里,有可能放在字符串池(String Pool)中;

String 的不可变特性体现在哪些方面呢?

首先,String 类是一个 final,它不能被继承

public final class String implements Serializable, Comparable<String>, CharSequence {
   ** 用来存储字符 *private final char[] value; 
	......
}

其次,它的内部真正用来存储字符的 value 数组也是一个 final 类型的,不能被修改指向

此外,String类中所有外部能调用的操作,凡是返回String对象的操作方法都是返回一个新的String实例对象

public String subString( int begin ) {
   
	......
	return (beginIndex == 0) ? this : new String(value, begin, subLen); 
}

也就是说,不管你怎么操作,最开始的String对象一定是不变的,你得到的所有String新实例都是新生成的(当然新生成的也是不可变的),而不会去修改原本的对象。

在这里插入图片描述

所以,总结一下String 的不可变特性主要体现在两个方面:

  • 不可变的类(包括数据成员)
  • 不可变的实例对象

二、String 为什么要设计成不可变的?

  1. 使用习惯需要:
String fileName = "demo.jar"; 
String dirPath = “disk/code/";
String filePath = dirPath + fileName;

String 实例的使用,主要是对字面量的使用,更接近基本数据类型。

  1. 计算需要:加快字符串处理速度
public final class String implements Serializable, Comparable<String>, CharSequence {
   ** 缓存字符串的hash code *private int hash;
	......
	public int hashCode() {
   
		int h = hash;
		if (h == 0 && value.length > 0) {
    
			char val[] = value;
			for (int i = 0; i < value.length; i++)	{
   
				h =(31 * h + val[i]; // 将每一位的特征在末位不断累加
			}
			hash = h; 
		}
		return h; 
	}
}

我们发现 Stringhash 值是计算出来以后缓存在内部的,假如 Stringvalue 数组可变,那么可能每次改变 value 数组都要重新计算 hash 值,效率降低。

由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String

  1. 线程安全需要:

例如以下代码:

public StringBuilder addSuffix(StringBuilder builder) {
    
	return builder.append(.jar");
}

// 这里的 StringBuilder 不具备线程可见性,且 StringBuilder 是可变类 
String fileNameBuilder = new StringBuilder("demo"); 
String fullFileName = "";

fullFileName = addSuffix(fileNameBuilder).toString(); // 并发执行 
fullFileName = addSuffix(fileNameBuilder).toString(); // 并发执行

在不加锁的前提下,假设有两个线程同时调用 addSuffix 方法并将结果赋值给 fullFileName ,那么 fullFileName 结果是 demo.jar 还是 demo.jar.jar?显然这无法得到线程安全的保证。

如果换成是 String,就能保证绝对线程安全,结果一定是 demo.jar

public String addSuffix(String str) {
    
	return str + ".jar" ;
}

String fileName = "demo"; 
String fullFileName = "";

full
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值