2、String()构造函数的方式
String str = new String("pingtouge")
的形式,使用这种方式创建字符串对象过程就比较复杂,分成两个阶段,首先在编译时,字符串pingtouge
会被加入到常量结构中,类加载时候就会在常量池中创建该字符串。然后就是在调用new()时,JVM 将会调用String
的构造函数,同时引用常量池中的pingtouge
字符串,
在堆内存中创建一个String
对象并且返回堆中的引用地址。
了解了String
对象两种创建方式,我们来分析一下下面这段代码,加深我们对这两种方式的理解,下面这段代码片中,str
是否等于str1
呢?
String str = "pingtouge";
String str1 = new String("pingtouge");
system.out.println(str==str1)
我们逐一来分析这几行代码,首先从String str = "pingtouge"
开始,这里使用了字符串常量的方式创建字符串对象,在创建pingtouge
字符串对象时,JVM会去常量池中查找是否存在该字符串,这里的答案肯定是没有的,所以JVM将会在常量池中创建该字符串对象并且返回对象的地址引用,所以str
指向的是pingtouge
字符串对象在常量池中的地址引用。
然后是String str1 = new String("pingtouge")
这行代码,这里使用的是构造函数的方式创建字符串对象,根据我们上面对构造函数方式创建字符串对象的理解,str1
得到的应该是堆中pingtouge
字符串的引用地址。由于str
指向的是pingtouge
字符串对象在常量池中的地址引用而str1
指向的是堆中pingtouge
字符串的引用地址,所以str
肯定不等于str1
。
String 对象的不可变性
从我们知道String
对象的那一刻起,我想大家都知道了String
对象是不可变的。那它不可变是怎么做到的呢?Java
这么做能带来哪些好处?我们一起来简单的探讨一下,先来看看String
对象的一段源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
}
从这段源码中可以看出,String
类用了 final 修饰符,我们知道当一个类被 final 修饰时,表明这个类不能被继承,所以String
类不能被继承。这是String
不可变的第一点
再往下看,用来存储字符串的char value[]
数组被private
和final
修饰,我们知道对于一个被final
的基本数据类型的变量,则其数值一旦在初始化之后便不能更改。这是String
不可变的第二点。
Java 公司为什么要将String
设置成不可变的,主要从以下三方面考虑:
- 1、保证 String 对象的安全性。假设 String 对象是可变的,那么 String 对象将可能被恶意修改。
- 2、保证 hash 属性值不会频繁变更,确保了唯一性,使得类似 HashMap 容器才能实现相应的 key-value 缓存功能。
- 3、可以实现字符串常量池
String 对象的优化
字符串是我们常用的Java
类型之一,所以对字符串的操作也是避免不了的,在对字符串的操作过程中,如果使用不当,性能会天差地别。那么在字符串的操作过程中,有哪些地方需要我们注意呢?
优雅的拼接字符串
字符串的拼接是对字符串操作使用最频繁的操作之一,由于我们知道String
对象的不可变性,所以我们在做拼接时尽可能少的使用+
进行字符串拼接或者说潜意识里认为不能使用+
进行字符串拼接,认为使用+
进行字符串拼接会产生许多无用的对象。事实真的是这样吗?我们来做一个实验。我们使用+
来拼接下面这段字符串。
String str8 = "ping" +"tou"+"ge";
一起来分析一下这段代码会产生多少个对象?如果按照我们理解的意思来分析的话,首先会创建ping
对象,然后创建pingtou
对象,最后才会创建pingtouge
对象,一共创建了三个对象。真的是这样吗?其实不是这样的,Java 公司怕我们程序员手误,所以对编译器进行了优化,上面的这段字符串拼接会被我们的编译器优化,优化成一个String str8 = "pingtouge";
对象。除了对常量字符串拼接做了优化以外,对于使用+
号动态拼接字符串,编译器也做了相应的优化,以便提升String
的性能,例如下面这段代码:
String str = "pingtouge";
for(int i=0; i<1000; i++) {
str = str + i;
}
编译器会帮我们优化成这样
String str = "pingtouge";
for(int i=0; i<1000; i++) {
str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}
可以看出 Java 公司对这一块进行了不少的优化,防止由于程序员不小心导致String
性能急速下降,尽管 Java 公司在编译器这一块做了相应的优化,但是我们还是能看出 Java 公司优化的不足之处,在动态拼接字符串时,虽然使用了 StringBuilder 进行字符串拼接,但是每次循环都会生成一个新的 StringBuilder 实例,同样也会降低系统的性能。
所以我们在做字符串拼接时,我们需要从代码的层面进行优化,在动态的拼接字符串时,如果不涉及到线程安全的情况下,我们显示的使用 StringBuilder 进行拼接,提升系统性能,如果涉及到线程安全的话,我们使用 StringBuffer 来进行字符串拼接
巧妙的使用 intern() 方法
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
public native String intern();
## 最后
需要的朋友可以点击:[**戳这里免费领取**](https://gitee.com/vip204888/java-p7)。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/45a690aa3ee11b20193494de64ae7cb1.png)