本文学习并总结java中的字符串。内容包括String字符串常用方法、StringBuffered和StringBuilder功能介绍,学习中练习了论坛中的一些提问问题,同时也对所学知识进行了验证巩固。
String类
String是java的字符串类,被final修饰,不能被继承,java 程序中的所有字符串字面值(如"abc" )都作为此类的实例实现。
java中字符串是常量,一旦被初始化就不可以被改变,对String对象的任何改变,都是返回一个新的String对象。
字符串常量池
先说下常量池,每个类都有相应的常量池,常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,字符串常量池是常量池的一部分。
jvm启动前,一个类的字符串常量池信息就已经被确定,写在.class文件中,jvm启动后,字符串常量池被加载到内存中的方法区。
字符串常量池中,每个字符串常量都是唯一的,没有相同字面值的多个副本。
字符串常量池由String类私有地维护。
程序运行时,可以通过String类的intern()方法动态地扩充字符串常量池。当调用 intern 方法时,如果池已经包含一个等于此String对象的字符串(用 equals(object)方法确定),则返回池中的字符串。否则,将此String 对象添加到池中,并返回此String对象的引用。所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。
public class StringDemo1 {
public static void main(String[] args) {
String s1="abc";//创建String型引用变量s1,指向字符串常量池中的"abc"字符串。
String s2=new String("abc");//栈中创建s2引用变量,堆内存中创建"abc"字符串对象,并被s2指向。
String s3="a"+"bc";//"a"和"bc"合成为字符串常量"abc",先在字符串常量池中查找是否已有"abc"字符串对象,已有,不再重新创建对象,s3和s1指向的是字符串常量池中的同一个对象,s1==s3.
String s4="a"+new String("bc");//在堆中创建了"bc"对象,与"a"相加,返回新的字符串对象赋给s4,s4还指向堆中的字符串对象。
System.out.println(s1==s2);//false
System.out.println(s1==s3);//true
System.out.println(s1==s4);//false
System.out.println(s2==s4);//false
System.out.println(s1.equals(s2));//true
System.out.println("=============================");
String s5=new String("def");//实际创建了2个对象,编译完成后字符串常量池中就有了"def",运行时在堆内存中创建String类对象"def".
String s6="def";
System.out.println(s5==s6);//false, s5指向堆内存中的"def"对象,s6是指向字符串常量池中的"def"字符串对象。
System.out.println(s6==s5.intern());//true, s5.intern()指向字符串常量池中的"def"字符串对象。
}
}
字符串常见操作
1. 获取
1) 获取字符串的长度: int length()
2) 根据位置获取该位置上的字符:
char charAt(int index),index越界时,会发生StringIndexOutOfBoundsException异常。
3) 根据字符(子串)获取该字符(子串)在串中的位置:
int index(int ch), 参数为字符的ASCII码,返回ch在字符串中第一次出现的位置,字符串中没有ch时返回-1;
int index(int ch,int fromIndex), 从fromIndex位置开始搜索,没有时返回-1;
int index(String str), 获取字符串str在另一个字符串中第一次出现的位置,没有时返回-1;
int lastIndex(int ch), 反向搜索,返回ch最后出现的位置。
2. 判断
1) 字符串是否包含某一子串:
boolean contains(String str), 其实通过index(String str)方法完全可以实现此功能;
2) 字符串是否以指定内容开头:boolean startWith(String str) ;
3) 字符串是否以指定内容结尾:boolean endWith(String str) ;
以上3个方法合起来可以实现常见的文件名搜索。
4) 字符串是否有内容:boolean isEmpty(), jdk1.6版本开始才有,此方法的原理就是判断字符串长度是否为0;
5) 判断字符串内容是否相同:boolean equals(String str);
6) 判断字符串内容是否相财,忽略大小写: boolean equalsIgnoreCase(String str);
3. 转换
1) 将字符数组转成字符串
可以使用String类构造函数:String(char[]), String(char[],offset,count);
还可以使用String类的静态方法:static String copyValueOf(char[])
static String copyValueOf(char[],offset,count)
static String valueOf(char[])
2) 将字符串转成字符数组:char[] toCharArray()
3) 将字节数组转成字符串:与字符数组转字符串基本完全一样,将char[]换成byte[]即可
4) 将字符串转成字节数组:byte[] getBytes[]
字符串和字节数组转换时,可以指定编码表,这里穿插说一会编码、解码的知识。
编码:字符串变成字节数组。解码:字节数组变成字符串。
常用的几种字符集
Ascii码字符集,用1个字节来表示,第1位为0,剩余7位表示总共127个字符,这显然不能满足英语之外的很多语言;
ISO8859-1编码,欧洲编码表,单字节编码,ISO 8859-1兼容ASCII码,在ASCII码空置的0xA0-0xFF的范围内,加入了96个字母及符号,藉以供使用变音符号的拉丁字母语言使用,ISO 8859-1又称Latin-1;
GB2312/GBK/GBK18030, 汉字的国标码,用2个字节表示一个汉字。GB2312只用表示简体字,包含6千多个汉字,GBK完全反向兼容GB2312编码,GBK编码能够用来同时表示繁体字和简体字,包含2万多个汉字,GBK还可编码英文字母,英文字母编码兼容iso8859-1编码(英文字母用一个字节表示)。GBK后来又扩展成GBK18030.
Unicode,国际标准码值,所有字符用定长双字节表示(定长编码计算机处理方便),可以用来表示所有语言的字符,包括英文字母;unicode不兼容任何编码,不过,相对于iso8859-1编码来说,Uniocode编码只是在前面增加了一个0字节,比如字母'a'为"00 61"。
UTF-8,国际标准码值,可以用来表示所有语言中的字符;变长字节编码,每一个字符的长度从1-3个字节不等,能用一个字节装下的就用一个字节表示,依次类推。一般来说,英文字母都是用一个字节表示,兼容iso8859-1,而汉字使用三个字节,同一个汉字在UTF-8和GBK中的编码可能不一样。
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class EncodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String a="你好";
byte[] b1=a.getBytes("GBK"); //编码默认字符集就是GBK,所以可不写。
System.out.println(Arrays.toString(b1));
String s1=new String(b1,"GBK");//解码默认字符集也是"GBK",此处"GBK"也可以不写.
System.out.println(s1);
byte[] b2=a.getBytes("UTF-8"); //使用UTF-8字符集编码
System.out.println(Arrays.toString(b2));//b1长度是6,UTF-8中一汉字占3个字节。
String s2=new String(b2,"GBK");//UTF-8编码的byte数组,用GBK解码,每2个字节去匹配GBK解码表,出现乱码
System.out.println(s2);
//byte[] b3=a.getBytes("ISO8895-1"); //运行后报异常java.io.UnsupportedEncodingException;因为iso8859-1字符集中没法识别中文字,不能对汉字编码;字符串一旦编码失败,就再也不能解码成功了。
byte[] b4=a.getBytes("GBK");
System.out.println(Arrays.toString(b4));//b1长度是6,UTF-8中一汉字占3个字节。
String s4=new String(b4,"ISO8859-1");//编码成功,但解码错误 ,可以补救,将b4先用iso8895-1解码,然后再用GBK解码。
System.out.println(s4);
byte[] b5=s4.getBytes("ISO8859-1");
System.out.println(Arrays.toString(b5));
String s5=new String(b5,"GBK");
System.out.println(s5);;
}
}
运行结果:
[-60, -29, -70, -61]
你好
[-28, -67, -96, -27, -91, -67]
浣犲ソ
[-60, -29, -70, -61]
????
[-60, -29, -70, -61]
你好
5) 将基本数据类型转成字符串: static String valueOf(int/byte/double等)。
4. 替换
String replace(oldChar,newChar), String replace(oldStr,newStr), 如果要替换的字符或字符串不存在,则返回原串。
5. 切割
String[] split(String regex);
注意,若用“.”和“|”切割字符串,因这2者都是转义字符,必须得加"\\",即str.split("\\.")和str.split("\\|")。
6. 获取子串
1) String subString(begin,end),返回的子串角标含begin,但不含end,含头不含尾;
2) String subString(begin), 等价于subString(begin, str.length())。
7. 去除空格,比较
1) 将字符串转成大写或小写: String toUpperCase(), String toLowerCase()
2) 将字符串两端的多个空格去掉:String trim()
3) 对2个 字符串进行自然顺序的比较:
int CompareTo(str), 相等时返回0,否则返回2个串中第1个不相等的字符的ASCII码值的差。
下面是String类相关的2个练习题,题目本身解决的问题也比较有意义,放在这里作个记录:
/*
问题:输入无重复字符的字符串,输出这些字符的全排列。
*/
/*
思路:基本就是按照数学中排列组合的原理,对多个字符排列时,可以递归分成第一个字符与剩余字符组成字符子串的全排列的组合。
*/
import java.lang.*;
import java.util.*;
class QuanPL{
public static void main(String[] args){
String str="abc";
// System.out.println((allMaxLen(str)).size());
// System.out.println(allMaxLen(str));
System.out.println(all(str));
}
//字符串全排列,长度从1到str.length()
public static ArrayList all(String str){
ArrayList al=new ArrayList();
if(str.length()==1){
al.add(str);
return al;
}else{
String sa=str.charAt(0)+"";
al.add(sa);
String sub=str.replace(sa,"");
//对子串进行递归
ArrayList tk=all(sub);
al.addAll(tk);
Iterator it=tk.iterator();
String s=null;
while(it.hasNext()){
s=it.next();
for(int j=0;j<=s.length();j++){
//遍历sa添加到s上时的位置
al.add(s.substring(0,j)+sa+s.substring(j,s.length()));
}
}
}
return al;
}
//长度为str.length()的字符串全排列。
public static ArrayList allMaxLen(String str){
ArrayList al=new ArrayList();
if(str.length()==1){
al.add(str);
return al;
}else{
char[] ch=str.toCharArray();
for(int i=0;i it=arr.iterator();
while(it.hasNext()){
String s=new String();
//String ss=it.next();
String re=ch[i]+it.next();//合起来就是整个字符串了
al.add(re);
}
}
return al;
}
}
}
/*
寻找2个字符串中相同的最大子串,如果有2个或多个不同的最大相同子串,放在一个ArrayList对象中全部返回。
*/
import java.util.*;
class Test5{
public static void main(String[] args){
String s1="kiokk";
String s2="kib3oktr";
System.out.println(maxSubstring(s1,s2));
}
public static ArrayList maxSubstring(String s1,String s2){
String max,min;
int len=0; //用于保存最大子串的长度
ArrayList al=new ArrayList(); //用于存储最大子串
max=(s1.length()>s2.length())? s1:s2;
min=(max==s1)? s2:s1;
System.out.println(max+"....."+min);
for(int x=0;x=len){ //代码首先产生的相同子串的长度肯定是最大的,所以这样判断 没问题,后续还有相同长度的子串时也加入到al中
len=temp.length();
al.add(temp);
}
}
}
}
return al;
}
}//运行结果会将输出ok,kk.
StringBuffer类
字符串缓冲区,是一个容器, final类,不能被继承。
特点:长度可变化;可以直接操作多个数据类型;最终会通过toString()方法转成字符串。
StringBuffer作为一个容器,可以进行增删改查等操作。
1. 存储
StringBuffer append(), 参数可以为String对象、StringBuffer对象、6种基本数据类型(除byte和short)、字符数组(可以指定offset和count),将指定参数添加到已有数据结尾处,返回原对象。
StringBuffer insert(index, 数据), 将数据插入到指定index位置,index超过字符串当前最大位置时,会有角标越界异常。
2. 删除
StringBuffer delete(start,end),删除缓冲区中的数据,含头不含尾
StringBuffer delete(charAt(index)), 删除指定位置上的字符
StrubgBuffer delete(0,sb.length()),清空缓冲区
3.获取
char charAt(int index)
int indexOf(String str)
int lastIndexOf(String str)
int length()
String subString(int start,int end)
4. 修改
void setCharAt(int index,char c)
StringBuffer replace(start,end,String str)
5. 反转
StringBuffer reverse()
6. getChars()方法
void getChars(int srcBegin,int srcEnd,char[] det,int detBegin), 将缓冲区中指定数据存储到目标字符数组中。
StringBuffer的一个巧妙应用示例:
/*
问题:把abcd...s共19个字母组成的序列重复拼接106次,得到长度为2014的串。
接下来删除第1个字母(即开头的字母a),以及第3个,第5个等所有奇数位置的字母。
得到的新串再进行删除奇数位置字母的动作。如此下去,最后只剩下一个字母,请写出该字母。
*/
public class StringBufferDemo
{
public static void main(String[] args)
{
String str = "abcdefghijklmnopqrs";
String ss=new String();
//将字符串拼接106次
for (int i = 0; i < 106; i++)
{
ss=ss+str;
}
System.out.println(remove(ss));
}
public static String remove(String str){
StringBuffer strbu=new StringBuffer(str);
while (1!= strbu.length()) //最后剩下一个字母,故循环结束条件为最后的长度为1
{
for (int i = 0; i < strbu.length(); i++)
{
/*如果将字符串看成一个字符数组,那么按照题目的意思,就是删除数组中所有下标为偶数(包括0)的字母,
* 但当删除当前i处的字母时,后边的字母会向前移动一个字符,这样,当前已被删的字母的下一个字母其实已经移到i处了,而此时i已经自增了1,
* 所以i刚好就是下下个字母的索引,也就是下一个偶数位位置。
*/
strbu.deleteCharAt(i); //删除索引i处的字母
}
}
return strbu.toString(); //最后结果是q
}
}
StringBuild类
是StringBuffer的一个简易替换,jdk1.5版本起才有的。
与StringBuffer的不同之处:
StringBuffer是同步的,线程安全,但每次操作时都要判断锁,效率低
StringBuilder是线程不同步的,不用判断锁,效率高,线程安全性要求高时可以手动同步。
开发建议使用StringBuilder.