String
java.lang.Object
java.lang.String
All Implemented Interfaces:
Serializable, CharSequence, Comparable<String>
String是个字符串常量类
public static void main(String[] args) {
String str1 = new String("123");
String str2 = new String("123");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
String str3 = "123";
String str4 = "123";
// str3 和 str4 指向同一块内存
System.out.println(str3 == str4);
System.out.println(str3.equals(str4));
str3 = "234";
str4 = "456";
str4 = "789";
String str5 = "456";
}
String有自己的一块常量Pool,代码的内存模型如下:
常量Pool中的内存不会被垃圾回收,遇到大量叠加字符串的使用,如下:
public class TestString2 {
public static void main(String[] args) {
String str = "";
// 以下循环会被分配100块内存,但只有最后一块内存被使用,
// 而且String Pool中的内存不会被回收
// 所以如果进行大量字符串叠加,不要使用以下方法
for (int i = 0; i < 100; i++) {
str+=i;
}
System.out.println(str);
}
}
内存String Pool中会产生如下100块内存:
这种情况不要使用String来进行叠加,可以使用StringBuffer或StringBuilder
StringBuffer是线程安全的,StringBuilder是线程不安全的,但字符串一般不太考虑多线程,所以JDK5之后就引入了单线程的StringBuilder,用来更快处理字符串。
方法 | 描述 |
---|---|
public char charAt(int index) | 返回index对应的字符,index的范围是[0,length()-1],有越界异常判断 |
public int codePointAt(int index) | 返回index对应字符的Unicode码,index的范围是[0,length()-1],有越界异常判断 |
public int codePointBefore(int index) | 返回index对应字符前一位的Unicode码,index的范围是[1,length()],有越界异常判断 |
public int compareTo(String anotherString) | 按字典顺序比较两个字符串 此String对象按字典顺序位于参数字符串之前,结果是负整数。 此String对象按字典顺序位于参数字符串之后,结果是正整数 相同为0 |
public boolean contains(CharSequence s) | String实现CharSequence接口,可以直接 s.contains("we") |
public boolean contentEquals(CharSequence cs) | String实现CharSequence接口,可以直接s.contentEquals("welcome") |
public boolean contentEquals(StringBuffer sb) | String s = new String("welcome");StringBuffer s2 = new StringBuffer("welcome"); System.out.println(s.contentEquals(s2)); 返回true |
public static String copyValueOf(char[] data) | 与 valueOf(char[]) 一致 |
public static String join(CharSequence delimiter, CharSequence… elements) | 返回一个以delimiter为分隔,其他元素都join一起的字符串,注意:如果一个元素是null,那也会返回,delimiter为null,返回NullPointerException |
public String[] split(String regex) | 字符串切割成字符串数组 split(“.”)改成split(“\.”) |
public String toLowerCase() | 小写字母转换 |
public boolean matches(String regex) | 字符串是否匹配正则表达式 |
public CharSequence subSequence(int beginIndex, int endIndex) | 截取字符串的下标范围[beginIndex,endIndex] |
public char[] toCharArray() | 转换字符串为字符数组 |
public static String valueOf(boolean b) | valueOf()方法有很多参数可以参考 |
intern()
尽管在输出中调用intern方法并没有什么效果,但是实际上后台这个方法会做一系列的动作和操作。在调用”ab”.intern()方法的时候会返回”ab”,但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。
可以看下面一个范例:
String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
System.out.println(str5.equals(str3));
System.out.println(str5 == str3);
System.out.println(str5.intern() == str3);
System.out.println(str5.intern() == str4);
输出:
true
false
true
false
为什么会得到这样的一个结果呢?我们一步一步的分析。
-
str5.equals(str3)这个结果为true,不用太多的解释,因为字符串的值的内容相同。
-
str5 == str3对比的是引用的地址是否相同,由于str5采用new String方式定义的,所以地址引用一定不相等。所以结果为false。
-
当str5调用intern的时候,会检查字符串池中是否含有该字符串。由于之前定义的str3已经进入字符串池中,所以会得到相同的引用。
-
当str4 = str1 + str2后,str4的值也为”ab”,但是为什么这个结果会是false呢?(这是因为str4是由两个变量连接的,含有变量的连接是不会进入字符串池的)先看下面代码:
String a = new String("ab"); String b = new String("ab"); String c = "ab"; String d = "a" + "b"; String e = "b"; String f = "a" + e; System.out.println(b.intern() == a); System.out.println(b.intern() == c); System.out.println(b.intern() == d); System.out.println(b.intern() == f); System.out.println(b.intern() == a.intern()); 输出: false true true false true
由运行结果可以看出来,b.intern() == a和b.intern() == c可知,采用new 创建的字符串对象不进入字符串池,并且通过b.intern() == d和b.intern() == f可知,字符串相加的时候,都是静态字符串的结果会添加到字符串池,如果其中含有变量(如f中的e)则不会进入字符串池中。但是字符串一旦进入字符串池中,就会先查找池中有无此对象。如果有此对象,则让对象引用指向此对象。如果无此对象,则先创建此对象,再让对象引用指向此对象。
当研究到这个地方的时候,突然想起来经常遇到的一个比较经典的Java问题,就是对比equal和==
的区别,当时记得老师只是说==
判断的是“地址”,但是并没说清楚什么时候会有地址相等的情况。现在看来,在定义变量的时候赋值,如果赋值的是静态的字符串,就会执行进入字符串池的操作,如果池中含有该字符串,则返回引用。
执行下面的代码:
String a = "abc";
String b = "abc";
String c = "a" + "b" + "c";
String d = "a" + "bc";
String e = "ab" + "c";
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a == d);
System.out.println(a == e);
System.out.println(c == d);
System.out.println(c == e);
输出:
true
true
true
true
true
true
总结:
1.采用new 创建的字符串对象不进入字符串池
2.字符串相加的时候,都是静态字符串的结果会添加到字符串池
3.如果其中含有变量,则不会进入字符串池中
split()
split使用regex匹配,需要注意特殊符号的转义:
public static void main(String[] args) {
String str = "hello|world|how|are|you.thanks.and you.*i*am*fun\\what\\";
String[] split = str.split("\\|");
for (String s : split) {
System.out.println(s);
}
System.out.println("---------------.-----------------");
split = str.split("\\.");
for (String s : split) {
System.out.println(s);
}
System.out.println("----------------*----------------");
split = str.split("\\*");
for (String s : split) {
System.out.println(s);
}
System.out.println("-----------------\\\\---------------");
split = str.split("\\\\");
for (String s : split) {
System.out.println(s);
}
}
substring()
substring(int beginIndex, int endIndex)
返回一个指针下标以 beginIndex开始,扩展到endIndex-1的字符串。