String基础
-
String类又称作不可变字符序列。
-
String位于java.lang包中,Java程序默认导入java.lang包下的所有类。
-
Java字符串就是Unicode字符序列,例如字符串“Java”就是4个Unicode字符’J’、’a’、’v’、’a’组成的。
-
Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。
【示例5-25】String类的实例
String e = "" ; // 空字符串
String greeting = " Hello World ";
- Java允许使用符号"+"把两个字符串连接起来。
【示例5-26】字符串连接
String s1 = "Hello";
String s2 = "World! ";
String s = s1 + s2; //HelloWorld!
n-符号"+"把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。
n-当"+"运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。
【示例5-27】"+"连接符
int age = 18;
String str = "age is" + age; //str赋值为"age is 18"
//这种特性通常被用在输出语句中:
System.out.println("age is" + age)
String类和常量池
在Java的内存分析中,我们会经常听到关于“常量池”的描述,实际上常量池也分了以下三种:
-
全局字符串常量池(String Pool)
全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。
-
class文件常量池(Class Constant Pool)
class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用。
-
运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。
【示例5-28】常量池
String str1 = "abc";
String str2 = new String("def");//理解:生成两个实例,一个是str2是生成“def”(String类)对象的
//引用,另一个是new出来了一个对象
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);// true
System.out.println(str2 == str4);// false
System.out.println(str4 == str5);// true
示例5-28的首先经过编译之后,在该类的class常量池中存放一些符号引用,然后类加载之后,将class常量池中存放的符号引用转存到运行时常量池中,然后经过验证,准备阶段之后,在堆中生成驻留字符串的实例对象(也就是上例中str1所指向的“abc”实例对象),然后将这个对象的引用存到全局String Pool中,也就是String Pool中,最后在解析阶段,要把运行时常量池中的符号引用替换成直接引用,那么就直接查询String Pool,保证String Pool里的引用值与运行时常量池中的引用值一致,大概整个过程就是这样了。
回到示例5-28的那个程序,现在就很容易解释整个程序的内存分配过程了,首先,在堆中会有一个“abc”实例,全局String Pool中存放着“abc”的一个引用值,然后在运行第二句的时候会生成两个实例,一个是“def”的实例对象,并且String Pool中存储一个“def”的引用值,还有一个是new出来的一个“def”的实例对象,与上面那个是不同的实例,当在解析str3的时候查找String Pool,里面有“abc”的全局驻留字符串引用,所以str3的引用地址与之前的那个已存在的相同,str4是在运行的时候调用intern()函数,返回String Pool中“def”的引用值,如果没有就将str2的引用值添加进去,在这里,String Pool中已经有了“def”的引用值了,所以返回上面在new str2的时候添加到String Pool中的 “def”引用值,最后str5在解析的时候就也是指向存在于String Pool中的“def”的引用值,那么这样一分析之后,结果就容易理解了。
理解:String str2 = new String(“def”); 这条指令创建了两个对象,一个是堆中的String对象,另一个是在常量池中的“def”;
而intern方法的作用是:是查看常量池中是否存在和调用方法的字符串内容一样的字符串,如果有的话,就返回该常量池中的字符串,若没有的话,就在常量池中写入一个堆中该字符串对象的一个引用,指向堆中的该对象,并返回该引用。
因此,此时常量池中已经存在了str2的相同内容“def”字符串,调用intern方法返回对常量池中该字符串的引用,因此,str2一个是堆内存中字符串对象的引用,str4则是指向String Pool常量池中String内容“def”的引用,虽然字符串内容一样,但是两者引用不同,因此指令 str2==str4 返回 false。
String类常用的方法
表5-2 String类的常用方法列表
字符串相等的判断
-
equals方法用来检测两个字符串内容是否相等。如果字符串s和t内容相等,则s.equals(t)返回true,否则返回false。
-
要测试两个字符串除了大小写区别外是否是相等的,需要使用equalsIgnoreCase方法。
-
判断字符串是否相等不要使用"=="。(这是比较引用是否指向同一个对象)
【示例5-31】忽略大小写的字符串比较
"Hello".equalsIgnoreCase("hellO");//true
【示例5-32】字符串的比较"=="与equals()方法
public class TestStringEquals {
public static void main(String[] args) {
String g1 = "北京尚学堂";
String g2 = "北京尚学堂";
String g3 = new String("北京尚学堂");
System.out.println(g1 == g2); // true 指向同样的字符串常量对象
System.out.println(g1 == g3); // false g3是新创建的对象
System.out.println(g1.equals(g3)); // true g1和g3里面的字符串内容是一样的
}
}
执行结果如图5-33所示:
图5-33 示例5-32运行效果图
示例5-32的内存分析如图5-34所示:
图5-34 示例5-32内存分析图
引用自:https://www.sxt.cn/Java_jQuery_in_action/five-string-basics.html
引用自:https://www.sxt.cn/Java_jQuery_in_action/five-constant-pool.html