1.String为什么是一个不可变类?
- 该类一旦初始化,其值不变,因为该类使用final修饰(final的用法说明,请看下面注)
- 并且用于存储数据的是使用char类型的数组存储,并且该成员变量使用final修饰,没有提供公共的setter和getter方法
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
2.String不可变类的好处?
- 可以作为形式参数,该String类型的引用变量不变,那么使用他的Hash值则只需要计算一次就可以了,不需要重复计算并且值唯一,例如作为HashMap中的key值可以保证唯一性
- 可以作为StringPool中的值,即为常量池中的值,如果该值在常量池中有,那么直接使用,不需要重复创建
- String经常作为参数,String不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果String是可变的,那么在网络连接过程中,String被改变,改变String对象的那一方以为现在连接的是其他主机,而实际情况却不一定是。
- String的安全性,String的不可变性,可以在多线程中使用。
3.String是不可变类,那么String a=“Hello”+“Word”+"!!!"或者String a=“Hello” +new String(“Word !!!”)岂不是+或者+=改变了该类的不可变性?如果没有那么如何处理的那?;
首先对于String a=“Hello”+“Word”+"!!!"这个字符串编译会帮我们处理成String a=“Hello Word !!!”;因此还是保证了该类的不可变性。下面是反编译的结果可以看出
0 ldc #2 <Helloword!!!>
2 astore_1
3 return
对于String a=“Hello” +new String(“Word !!!”)这个字符串,java虚拟机会创建一个StringBuilder对象,然后通过appand方法将字符串存储到自己的数组中,然后通过toString方法返回给a因此+号是不违反不可变类特性的。以下是反编译代码
new #3 <java/lang/StringBuilder>
6 dup
7 invokespecial #4 <java/lang/StringBuilder.<init> : ()V>
10 ldc #5 <Hello>
12 invokevirtual #6 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
15 new #7 <java/lang/String>
18 dup
19 ldc #8 <word !!!>
21 invokespecial #9 <java/lang/String.<init> : (Ljava/lang/String;)V>
24 invokevirtual #6 <java/lang/StringBuilder.append : (Ljava/lang/String;)Ljava/lang/StringBuilder;>
27 invokevirtual #10 <java/lang/StringBuilder.toString : ()Ljava/lang/String;>
30 astore_2
31 return
4.那么对于String a=new String(“Hello Word !!!”)和String a=“Hello Word !!!” 分别创建几个对象?![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5cb65895f95c8ef46162bca662f1c75b.png#pic_center)
- String a="Hello Word !!!“首先会看StringPool中是否存在"Hello Word !!!”,如果不存在那么会在StringPool中创建并分配地址,然后将栈中的a指向StringPool中的地址值。
- String a=new String(“Hello Word !!!”)首先会判断Stringpool中是否存在该字符串,如果存在,那么会在堆中分配该字符串的地址,然后该地址指向StringPool中的地址,最后栈中的a指向堆中分配资源的地址。
所以使用equals方法比较的是内容是否相同,因此返回true,但是==比较的是内存地址值是否相同,因此返回是false。一个是堆中的地址值一个是StringPool中的地址值
5.String a=“Hello Word !!!” 和String a=new String(“Hello Word !!!”) 采用equals和==比较的结果值如何以及原因?
通过4的分析可以看出==比较时地址值是不同的,所以返回的是false;equals比较的是值是否相同,因此返回的是true,因为String对Object类中对equals方法进行了重写。
5.StringBuilder是什么?##
- StringBuilder是字符串的缓存类,因此是可变的
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
2.是通过append方法追加到字符数组中的。
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
6.String和StringBuilder以及StringBuffer该如何选择?
类 | 效率 | 是否可变 | 是否安全 |
---|---|---|---|
String | 低 | × | √ |
StringBuffer | √ | √ | √ |
StringBuilder | √ | √ | X |
1.StringBuffer是线程安全的,因为他的方法是采用synchornized(同步锁机制)修饰,因此是安全的,但是他是可变的。
2.StringBuilder是线程不安全的,没有同步锁机制。但是是可变的。
- 当使用少量数据时可以使用String类,因为如果大量数据使用+号连接字符串,那么会底层频繁的创建StringBuilder对象,导致内存开销大。
- 当使用大量数据时,不需要考虑线程安全的问题时,那么可以使用StringBuilder对象,通过append方法追加。
- 当使用大量数据时并且考虑多线程环境时,那么需要使用StringBuffer对象,也可以使用StringBuilder对象,但是需要我们自己加锁机制保证多线程环境下安全。
7.StringPool是什么?
JVM为了提升性能和减少内存开销,避免字符串的重复创建,维护了一块特殊的内存空间,即字符串常量池(StringPool),它保存着所有字符串字面量,这些字面量在编译时期就确定,还可以使用String的intern()方法在运行过程中将字符串添加到池中。
当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。
注:final的用法? 修饰类、属性、方法。
- final可以修饰类,表示该类不能被继承。所以该类没有子类
- final可以修饰属性,
- 修饰普通成员变量,则表示常量,必须直接初始化或者构造函数初始化或者代码块初始化
- 修饰静态成员变量,则表示常量,必须直接初始化或者静态代码快初始化
- 修饰形式参数,则表示在该作用域内只允许读、不允许修改
- final可以修饰方法,则表示该方法在类被继承后不能被重写。
- final修饰的引用类型变量时,该变量不允许指向其他。