String类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
首先来看一个关于String示例:
String s1 = "HelloWorld";
String s2 = "HelloWorld";
String s3 = new String("HelloWorld");
String s4 = "Hello";
String s5 = "World";
String s6 = "Hello" + "World";
String s7 = s3 + s4;
System.out.println(s1 == s2);//1:输出为true
System.out.println(s1 == s3);//2:输出为false
System.out.println(s1 == s6);//3:输出为true
System.out.println(s1 == s7);//4:输出为false
System.out.println(s1 == s7.intern());//5:输出为true
System.out.println(s3 == s3.intern());//6:输出为false
输出结果:
看了输出结果,你可能会有很多疑惑,没关系,我们对输出结果分别进行分析:
一、为什么输出语句1输出为true,而输出语句2却输出为false?
在解析这个问题之前,我们先来了解一下JVM内存中的栈(stack)、堆(heap)和方法区:
► 栈中保存的是:基本数据类型的变量、对象的引用、函数调用的现场。
► 堆中保存的是:通过new关键字和构造器创建的对象。堆是垃圾收集器管理的主要区域。
► 它们之间的区别是:栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemoryError。
► 举例说明:String str = new String(“hello”)这个语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量是放在方法区的。
JVM中内存分布:
了解完内存后我们来解决这个问题,String实例的创建方式主要是以下两种:
- 直接赋值:
String s1 = "HelloWorld"
- 通过new关键字来创建:
String s3 = new String("HelloWorld")
这两种方式的内存示意图:
这两种创建方式的区别是它们产生实例的过程,以上面示例进行演示:1.直接赋值会先检查字符串常量池中是否含有HelloWorld字符串,如果HelloWorld字符串已经存在,那么就直接指向; 如果没有,则将HelloWorld添加进常量池并且指向。
2.通过new关键字来创建,会向堆申请内存,来存储HelloWord字符串对象,指向的是堆中的字符串对象。通过new操作符创建的字符串对象不指向字符串池中的任何对象,采用new 创建的字符串对象也不会进入字符串池。
输出语句1比较的是s1和s2,在执行String s1="HelloWorld"
时会将“HelloWorld”添加进常量池,s2赋值时“HelloWorld”已经存在于常量池中,因此s1和s2指向常量池中同一个字符串,所以输出为true。而输出语句比较的是s1和s3,s3是通过new关键字创建的字符串,通过new操作符创建的字符串对象不指向字符串池中的任何对象,因此输出为false。
二、为什么输出语句3输出为true,而输出语句4却输出为false?
解析:字符串相加的时候,如果相加的都是静态字符串,那么跟直接赋值相同,会先检查字符串常量池中字符串是否存在,如果经存在,就直接指向,如果不存在,就将字符串添加进常量池并且指向【String s6="Hello"+"World"
跟String s1="HelloWorld"
实质上是相同的,String s6="Hello"+"World"
在编译阶段就会被处理成String s6="HelloWorld"
】,所以输出语句3输出为true;如果相加时包含了变量,如String s7=s3 + s4
中包含了s3,s4两个变量,那么就相当于通过new关键字来创建,因此输出语句4输出为false。
三、String中intern方法详解
当调用 intern 方法时,如果字符串常量池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
这就可以解释输出语句5和6的输出结果:
输出语句5比较的是s1跟s7.intern(),s1指向的是字符串常量中的“HelloWorld”,s7是由s3+s4拼接起来,字面量为“HelloWorld”,调用s7的intern()时,由于字符串常量池中已经包含了HelloWorld,因此返回的是已经存在的HelloWorld,跟s1所指向的是同一个对象,所以输出语句5输出的为true;
输出语句6中比较的是s3和s3.intern();s3是通过new关键字创建的对象,跟字符串常量池没有丝毫关系,因此输出为false。
四、String中常用的方法(了解更多方法请参考API)
(1)length() 字符串的长度
例:char chars[]={‘a’,’b’.’c’};
String s=new String(chars);
int len=s.length();
(2)charAt() 截取一个字符
例:char ch;
ch=”abc”.charAt(1); 返回’b’
(3)indexOf()和lastIndexOf()
indexOf() 查找字符或者子串第一次出现的地方。
lastIndexOf() 查找字符或者子串是后一次出现的地方。
(4)substring()
此方法有两个重载:String substring(int startIndex) 和String substring(int startIndex,int endIndex)
(5)替换:replace()
(6)trim() 去掉起始和结尾的空格