一、String类概述
1.String对象一旦创建就不能改变。
2.字符串常量池。
字符串常量池的特点:池中有则直接使用,池中没有则创建新的字符串常量。
例1:
public class StringDemo
{
public static void main(String args[])
{
StringDemo1(); }
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
以上的代码运行结果为true。
原因分析:当运行到代码String str1="abcd";处,JAVA虚拟机会先检查字符串常量池中是有相同的字符串,如果有,则返回该对象的引用,否则,新创建一个字符串并返回该对象的引用。
运行到代码String str2="abcd";的时候,JAVA虚拟机发现字符串常量池中有相同的字符串,所以不再创建而是返回该对象的引用。
例2:
public class StringDemo
{
public static void main(String args[])
{
//StringDemo1();
StringDemo2(); }
public static void StringDemo2()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1==str2);
}
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
上述运行结果为false。
分析:运行完代码String str1="abcd";的时候,字符串常量池中将会含有一个值为abcd的字符串;执行到代码String str2=new String("abcd");时,进行了两个动作,因为使用new方法创建字符串对象的时候,所需要的构造方法中需要一个对象来创建新对象,所以,"abcd"就是一个动作,它将会在字符串常量池中寻找相同值的对象并返回该对象的引用。但是String类的构造方法中并没有直接使用该对象,而是将该对象先转换成一个字符数组,然后将该字符数组重新组装成一个字符串对象。这个过程可以通过查看源代码获得。因此使用==比较两个对象结果为false,因为它们并不是一个对象,它们的地址值不同。
例3:
public class StringDemo
{
public static void main(String args[])
{
//StringDemo1();
//StringDemo2();
StringDemo3(); }
public static void StringDemo3()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1.equals(str2));
}
public static void StringDemo2()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1==str2);
}
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
该运行结果为true。
分析:先观察源代码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String类的equals方法复写了Object类的方法,因此它所接收的参数对象并不是String类型的,而是Object类型的,查看该源码,我们可以发现首先先比较两个对象的引用是否相同,如果相同,则返回true;这是合理的,毕竟如果两个对象的引用都相同,则两个对象一定是完全相同的。接下来,如果两个对象的引用不相同,则并不返回false,而是挨个比较两个字符串中的字符是否相同,如果比较完成之后完全相同,则返回true,否则返回false。我们最后得出结论,那就是该方法在String类中复写之后比较的是字符串内容是否相同,相同返回true,不同返回false。这里由于两个字符串内容相同,因此返回true。
例四:intern()方法。
public class StringDemo
{
public static void main(String args[])
{
//StringDemo1();
//StringDemo2();
//StringDemo3();
//StringDemo4();
//StringDemo5();
StringDemo6();
}
public static void StringDemo6()
{
String str1=new String("abcd");
String str2=str1.intern();
String str3="abcd";
System.out.println(str1==str2);
System.out.println(str2==str3);
}
public static void StringDemo5()
{
char buf[]={'A','B','C','D','E'};
String str=new String(buf);
System.out.println(str);
}
public static void StringDemo4()
{
byte buf[]={65,66,67,68,69};
String str=new String(buf);
System.out.println(str);
}
public static void StringDemo3()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1.equals(str2));
}
public static void StringDemo2()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1==str2);
}
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
输出结果为两行:
false
true
分析:当String对象调用intern方法时,JAVA虚拟机就会到字符串常量池中寻找字符串常量并和当前字符串内容进行比较,如果值相同,则返回该字符串常量池中的常量的引用,否则创建一个新的常量并返回该常量的引用。执行String str1=new String("abcd");的时候,字符串常量池中已经有了该常量。执行String str2=str1.intern();时,由于已经有了该常量,所以会返回该常量的引用。同理,Stirng str3="abcd";时也是如此。所以会出现这样的结果。
3.String类的构造方法。
我们经常使用String str="xxxx";的形式创建字符串对象,貌似String str=new String("xxx");的形式就没有用了,其实不然,毕竟这种方式存在即合理,很多时候使用前一种方式并不能解决问题,我们还是要使用后一种方式创建字符串,特别是在需要将其它数据类型转换成字符串类型的时候。
其它数据类型主要包括字符数组和字节数组。
3.1字节数组转换成字符串。
public class StringDemo
{
public static void main(String args[])
{
//StringDemo1();
//StringDemo2();
//StringDemo3();
StringDemo4(); }
public static void StringDemo4()
{
byte buf[]={65,66,67,68,69};
String str=new String(buf);
System.out.println(str);
}
public static void StringDemo3()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1.equals(str2));
}
public static void StringDemo2()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1==str2);
}
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
运行结果为:ABCDE。
3.2字符数组转换成字符串。
public class StringDemo
{
public static void main(String args[])
{
//StringDemo1();
//StringDemo2();
//StringDemo3();
//StringDemo4();
StringDemo5(); }
public static void StringDemo5()
{
char buf[]={'A','B','C','D','E'};
String str=new String(buf);
System.out.println(str);
}
public static void StringDemo4()
{
byte buf[]={65,66,67,68,69};
String str=new String(buf);
System.out.println(str);
}
public static void StringDemo3()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1.equals(str2));
}
public static void StringDemo2()
{
String str1="abcd";
String str2=new String("abcd");
System.out.println(str1==str2);
}
public static void StringDemo1()
{
String str1="abcd";
String str2="abcd";
System.out.println(str1==str2);
}
}
输出结果:ABCDE。
3.3其它构造方法。
String(byte[] bytes, int offset, int length);该方法将指定在offset处开始处的length长度范围内的字节转换成字符串。
String(char[] value, int offset, int count);该方法原理和上述相同。
3.4总结。
通过使用String类的构造方法可以实现字节数组和字符数组向String类对象的转换。
二、String类功能
1.获取。
1.1获取字符串字符的个数,即字符串长度。
int length();
1.2根据位置获取字符。char charAt(int index);
1.3根据字符(字符串)获取在字符串中的位置。
自前向后找:int indexOf(int ch);
int indexOf(int ch, int fromIndex);
int indexOf(String str, int fromIndex);
自后向前找:
int lastIndexOf(int ch);
int lastIndexOf(int ch, int fromIndex);
int lastIndexOf(String str);
int lastIndexOf(String str, int fromIndex);
查找的时候,应当注意不要越界,否则抛出异常。
如果没有查找到,通常返回-1,可以根据此判断字符或者字符串是否存在。
1.4 获取字符串中的一部分字符串。或者称为字串。
String substring(int beginIndex, int endIndex);
使用方法:subString,返回String,有两个参数,一个是beginIndex,一个是endIndex,截取的到是endIndex之前的一个字符。即beginIndex到endIndex-1。
String substring(int beginIndex)
;
重载方法有subString ,只有一个参数,表示从指定的位置开始,一直到字符串结尾。
2.转换。
2.1将字符串变成几部分。如果有这个功能将返回字符串数组。字符串切割。String[] split(String regex, int limit);
用法举例:
String s="张三、李四、王五";
String []arr=s.split(",");
如果不是,而是.,则需要特殊处理,因为.是正则表达式中的特殊字符。转义\\.
切割动作涉及到正则表达式。
2.2将字符串变成字符数组。Char[] toCharArray();
2.3将字符串变成字节数组。
Byte[] getBytes(String charsetName);
打碎成最小单位:字节。
用法举例:
String s="ab你";
byte []arr=str.getBytes();
输出的结果却是:英文字母变成数字输出,而中文则变成了两个负数。
中国的gb2312码最高位都是1,所以都是负数。
2.4字符串中的大小写转换。
String toUpperCase();将字符串中的小写字符转换成大写。
String toLowerCase();将字符串中的大写字符转换成小写字符。
2.5将字符串中的内容进行替换。String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);
CharSequence是String已经实现的接口。
2.6将字符串两端的空格去掉。String trim();
2.7将字符串进行连接。String concat(String str);
concat方法和+的作用差不多。
前者显得更加专业。
2.8value()方法。
此方法为String类的静态方法,参数为各种基本数据类型,作用是将基本数据类型转换成字符串。
3.判断。
3.1两个字符串内容是否相同?boolean equals(Object anObject);
boolean equalsIgnoreCase(String anotherString)
//忽略大小写进行比较,其实就是先转换成大写或者小写再进行比较。
3.2字符串中是否包含某个字符串?boolean contains(CharSequence s);
其实是用indexOf方法也可以达到相同的目的。
3.3字符串是否以指定字符开头,是否以指定字符串结尾。
boolean endsWith(String suffix);
boolean startsWith(String prefix);
4.比较方法。
int compareTo(String anotherString);
int compareToIgnoreCase(String str)
;
按照字典序比较两个字符串。
基本数据类型使用的是比较运算符,而对象比较使用的是compareTo方法。
三、StringBuffer类。
StringBuffer类:
就是字符串缓冲区,是用于存储数据的容器。
数组也是存储数据的容器,它和StringBuffer的区别是什么?
1.长度可变
2.可以存储不同类型的数据进来。
3.最终要转成字符串才能使用。
4.可以对字符串进行修改。
该类的构造方法中的参数是整数的时候,指定了该缓冲区的初始容量大小;该类的构造方法是字符串时,将会构造一个StringBuffer对象,并将其内容初始化为和该字符串相同。
StringBuffer类的功能:
1.添加。
append方法。参数是基本数据类型,只有两种不行:byte和short不行,但是有int可以代替。
初始容量为16个字符。
public class StringBufferDemo
{
public static void main(String args[])
{
Demo1();
}
public static void Demo1()
{
StringBuffer sb=new StringBuffer();
StringBuffer bs=sb.append(4);
System.out.println(sb==bs);
}
}
运行结果为true。
分析:一个容器加入了一些东西以后该容器仍然是该容器,并没有变化,所以结果为true。
insert方法。该方法参数分为两部分,一部分是插入的位置,一部分是插入的内容。
public class StringBufferDemo
{
public static void main(String args[])
{
//Demo1();
Demo2();
}
public static void Demo2()
{
StringBuffer sb=new StringBuffer();
sb.append("abba");
sb.insert(2,"XXXX");
System.out.println(sb);
}
public static void Demo1()
{
StringBuffer sb=new StringBuffer();
StringBuffer bs=sb.append(4);
System.out.println(sb==bs);
}
}
运行结果为:abXXXXba
2.删除。
方法:StringBuffer delete(int start, int end);
该方法包含头不包含尾。
StringBuffer deleteCharAt(int index);该方法删除指定位置的字符。
使用该方法可以清空缓冲区:sb.delete(0,sb.length());
清空缓冲区也可以使用sb=new StringBuffer();但是不推荐使用因为会浪费内存空间。
3.查找。
和String类几乎相同。
4.修改。
StringBuffer replace(int start, int end, String str);包含头不包含尾。该方法的参数个数和String相同,但是位置颠倒了。
void setCharAt(int index, char ch);该方法比较特殊,并没有返回StringBuffer对象。该方法将指定位置上的字符替换为指定字符。
void setLength(int newLength);在使用该方法时,如果设定的长度小于内容的长度,则会删除多余的部分。使用此方法可以达到清空StringBuffer对象的目的,但是不推荐使用。
StringBuffer reverse();反转字符串
四、StringBuilder类。
此类提供了一个与StringBuffer兼容的API。也就是功能用法一模一样。
这两个类有什么不同?
StringBuffer在jdk1.0就出现了,线程安全。
StringBuilder在jdk1.5才出现,线程不安全。
浅析StringBuffer类线程安全的原因:StringBuffer类中有append方法和delete方法,如果一个线程调用append方法,另一个线程同时调用delete方法,在不加同步锁的情况下,就会出现线程安全性问题。JDK1.0考虑的线程安全性多一点,所以加上了同步;使用同步使得线程更加安全,但是这样的好处仅仅在多线程编程中--如果是单线程,由于不会出现线程安全性问题,所以如果经常使用StringBuffer类的append方法和delete方法,就会极大的降低程序的执行效率,这是每次调用方法都必须判断锁造成的。
JDK1.5考虑到了这一点,所以将同步去掉,重新创建了一个类StringBuilder,这是考虑到程序执行效率之后的结果。
我们要知道JDK升级几乎只有三点原因:
1.简化书写
2.提高效率
3.增加安全性。
而StringBuilder类出现的目的正是为了提高效率,付出的代价就是不安全。