设计者为String类重载了“+”和“+=”操作符,使得拼接操作十分简单。Object类的toString方法,使得各个原子数据类型向字符串转化非常方便,想必大家在System.out.print的时候都体会过。
一、String对象的创建和保存
String是一个特殊的类,它允许像int,double等原子数据类型一样直接用“=”初始化,也允许像一般的类一样使用new来创建对象。比如:
String hello="Hello,world!";
String hi=new String("hi");
不论哪种方法,都会在常量池中保存。对于第一种方法,如果常量池中已经保存过“Hello,wolrd!”,就直接指向值为Hello,world!的对象的地址。如果不存在,则自动创建一个String类对象,保存在常量池中。也就是说,直接赋值不一定会创建新对象。而第二种方法除了完成第一种方法的步骤,一定会创建对象。
通过阅读源码,可以知道String类保存内容是通过一个字符数组,恩,简单粗暴,没什么神秘感。
private final char value[];
二、字符串简单操作:比较和拼接
(1)比较
两个字符串比较,使用“==”比较地址,equals方法比较内容。
例如下面的代码段:
String s1="hello";
String s2="hello";
String s3=new String("hello");
System.out.println("s1==s2:"+(s1==s2));
System.out.println("s1==s3:"+(s1==s3));
System.out.println("s1.equals(s3):"+s1.equals(s3));
没错,输出结果就是这样:
s1==s2:true
s1==s3:false
s1.equals(s3):true
new创建对象,地址是不同的,所以第二个输出了false。
String类型还可以直接用在switch语句中,比如:
switch(s1){
case "hello":
System.out.println("hello");
}
//从java7开始支持的新功能
(2)拼接
对于String类,其内容保存在常量池中,是不可变的,而且也可以看到String类并没有append方法, 那么String类是如何重载“+”操作符的呢?
其实是为新产生的值新创建了对象,借助StringBuilder类完成。比如:
String s1="hello";
String s2="worl";
String s3=s1+s2;
执行第三句时,编译器做了下面的工作:
String s1="hello";
String s2="world";
StringBuilder sb=new StringBuilder();
sb.append(s1).append(s2);
String s3=sb.toString();
//System.out.println(s3);
我们也可以显式的完成上面的代码段。另一个类StringBuffer,也可以用于拼接字符串,和StringBuilder类的区别在于StringBuffer是线程安全的。
了解了拼接操作的实现细节,这就产生了一个问题。拼接一个字符串还好,如果是在循环中,大量+操作,每次都会创建对象,这就会造成内存开销上升,效率降低。因此,在需要循环构造字符串时,建议在循环外创建StringBuilder或StringBuffer对象一次,循环中调用其append方法即可。
三、字符串类的常用方法
(1)length()
返回字符串长度。
(2)isEmpty()
length为0时返回true。length为0,对应的是”“这种字符串,而不是null。
(3)charAt(int index)
根据index返回字符。这个方法会抛出一个经典的异常,
StringIndexOutOfBoundsException - 如果 index 参数为负或大于此字符串的长度。
(4)getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
将字符串复制到字符数组。这个参数列表比较复杂,一般都用toCharArray方法。
(5)equals方法
(6)contentEquals(StirngBuffer sb)
是的,这是比较sb和string内容的方法。
(7) equalsIgnoreCase(String anotherString)
忽略大小写的字符串比较。
(8)compareTo(String anotherString)
返回0或大于小于0的数,类似于c语言里面的strcmp。其返回值是有意义的,不过一般还是只看正负。
(9) startsWith(String prefix)
测试此字符串是否以指定的前缀开始。返回布尔值。
(10)endsWith(String suffix)
测试此字符串是否以指定的后缀结束。返回布尔值。
(11)indexOf(int ch)
查找字符ch第一次出现的索引找不到返回-1。
(12)indexOf(String str)
查找字符串str第一次出现的索引,返回第一个字符位置,找不到返回-1。
(13) substring(int beginIndex, int endIndex)
返回字符串,这是截取子串的方法。原理比较简单,就是创建一个新的字符数组,然后根据索引拷贝。源码粘贴一发:
public String(char value[], int offset, int count) {
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
public String substring(int beginIndex, int endIndex) {
int subLen = endIndex - beginIndex;
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
(14)replace(char oldChar, char newChar)
返回替换了字符后的字符串。
(15) String[] split(String regex)
这是非常常用的方法。根据正则表达式拆分字符串。结尾的空串会被忽略,但开头的空串会保留。例如:
String longstr=",,apple,peach,,";
String strs[]=longstr.split(",");
System.out.println(strs.length);
输出4,空串,空串,apple,peach。
(16)valueOf(Objecy)
一组静态方法,可以方便的将原子数据类型等转化为字符串。用法:String.valueOf(xxx);
(17)String类型转化为其他类型
int,long,double等原子类型提供了parseXXX静态方法,将字符串转化为数字。和上面的valueOf正好是一对。
int a=1234567;
String astr=String.valueOf(a);
int b=Integer.parseInt(astr);
System.out.println(astr+" "+b);