Java String的那些事儿

这一节主要围绕Java中String类展开,不会说的太过详细,只是简单总结一下,那么对于String有哪些特性需要我们了解的?
(1)String是不可变(immutable)的。

查看Java源代码不难发现,Java类定义为final,且里边的大多数字段也是final的,如下图:

(2)两种创建String的方式和区别。

一般来说,我们会采取两种方式来创建String,一种是直接指向字符串常量,另一种是new String(...)。这两种究竟有什么区别,来看看下面这个例子:

String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");

System.out.println(s1 == s2);      // true
System.out.println(s1.equals(s2)); // true

System.out.println(s3 == s4);      // false
System.out.println(s3.equals(s4)); // true

System.out.println(s4 == s1);      // false
System.out.println(s4.equals(s1)); // true

System.out.println(s4.intern() == s1);      // true
System.out.println(s4.intern().equals(s1)); // true
关于equals(..)和==的区别,equals(..)比较两个字符串的value是否相同,==比较两个字符串是否指向同一个内存地址。当然,如果两个字符串指向同一个内存地址,调用equals(..)进行比较肯定会返回true。

从上面的demo不难发现,s1和s2指向同一内存地址,而s3和s4并不指向同一内存地址。这牵涉Java内存管理的知识,简单说来,我们通过第一种方式创建的字符串(即指向字符串常量的方式),它会返回string pool中"hello"字符串常量的引用,且string pool保证"hello"的唯一性。而通过第二种方式创建的字符串(即new String(...)),它会在heap中动态申请内存,调用一次则会重新申请一次,这样每次申请的内存地址会不一致。注意string pool和heap不是一个概念,不是同一段内存,这也就解释了s4和s1指向的内存地址不同。

我们注意到s4.intern()与s1的指向是相同的,说一下intern()方法:

public String intern()
Returns a canonical representation for the string object. 
A pool of strings, initially empty, is maintained privately by the class String. 

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. 

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. 

All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of the Java Language Specification 


Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
大致意思就是说,它会字符串在string pool中的地址,如果string pool中存在相等的字符串(equals()返回true)则直接返回该字符串引用;如果不存在则在string pool中创建字符串返回引用。那么,s4.intern()返回的地址正是s1引用的字符串地址。

(3)可变的字符串。

前面说到String是不可变的,但有时候我们需要可变的字符串来完成一些任务,通常采用的方式是用+号在字符串尾部添加另一段字符串,但需要注意的是,这种方式并不会在原有的字符串基础上进行修改,而是会创建新的字符串,即在heap中不断地new新的String,这种方式可想而知效率会很低。为了解决这类问题,JDK提供了两个类StringBuilder和StringBuffer。我们在使用时需要区分两者的使用范围,StringBuilder是非线程同步的,StringBuffer是线程同步的。换句话说,在多线程的情况下推荐使用StringBuffer,在单线程的情况下推荐使用StringBuilder。

在使用StringBuilder和StringBuffer的时候,我们还需要注意一个问题,如果预先清楚最终生成的字符串的大小,最好在初始化StringBuilder和StringBuffer时指定String的capacity,这样能够获得一定程度的性能提升。至于为什么要这样用,可以参考JDK源码关于这两个类的实现,提示与容量相关。

(4)String为什么设计为不可变?

可能有人会有疑问,为什么String设计为不可变,设计为可变不是会更方便?

在上面的那个例子中,s1和s2指向的同一个引用,如果此时s1发生了变化,当String设计为可变时,那么此时s2也会发生变化,这样会带来一些麻烦。

我们看到,在String类的定义中,有一个int字段hash,它是用来保存String对象的hashcode,如果String是可变的,需要反复调用hashCode方法计算hash值,而如果String是不可变的,那么能够保证hash值不会发生变化。它应用在HashMap的key中会更加高效。

同时,不可变的也是更加安全的,举个例子:打开文件时要指定文件路径,传入String引用,如果String是可变的,不能保证打开的文件是我们所想要的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值