一、String的特性
1、String是一个final类,代表不可变的字符序列.不可被继承。
2、字符串是一个常量,使用“ ”来表示, 他们的值在创建之后不能更改。
3、String实现了Serializable接口,表示字符串是支持序列化的
实现了Comparable接口,表示字符串可以比较大小
4、String 内部定义了final char[] value 数组用于存储字符串数据
5、String代表不可变的字符字符序列。简称:不可变性。体现:
- 当字符串重新赋值时,需要重新指定内存区域赋值,不能再原有的value赋值
- 当对现有的字符串惊醒连接操作时,也需要重新指定内存区域赋值,不能再原有的value赋值
- 当调用 String的replace()方法修改指定的字符串时,也需要重新指定内存区域赋值,不能再原有的value赋值
总结
String 通过字面量的啊方式(区别于new)给一个字符串赋值, 此时字符串 声明在 常量池中
字符串常量池中是不会 存储相同内容的字符串的
二、String的赋值(对象的创建)
详见示例
public void test3(){
//方式一
String str = "hello";
//方式二:本质上this.value = new char[0];
String s1 = new String("hello");
//方式三:this.value = original.value;
String s2 = new String(String original);
//方式四:this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
}
① String s1 = “abc”; 和 ②String = new String(“abc”);的区别?
在内存中的位置不同:
①在常量池中
②在堆空间中(且相同内容的字符串存放的位置不同 -->占用更多的内存空间)
三、面试题1 : 通过 String s = new string(“abc”); 方式创建对象时, 在内存中创建几个对象?
答案:2个 一个在堆空间中 另一个是char[] 对应常量池中的数据"abc"
public void test4(){
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s2 == s3);//false
System.out.println(s3 == s4);//false
Person p1 = new Person("Tom",19);
Person p2 = new Person("Tom",19);
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1 == p2);//true
//"Tom" 是放在常量池里的,只有一个 ”Tom“
}
特殊情况(final)
@Test
public void test(){
final String s1 = "abc";
String s2 = "abcdef";
String s3 = s1 + "def";
System.out.println(s2 == s3);//true
//加了final后s1 变为常量了 ,在常量中拼接
}
结论:
1、常量与常量的拼接结果在常量池,且常量池中不会存在相同的常量
2、只要拼接中有一个是变量,结果就在堆中
3、如果对拼接结果用 intern() ,返回值就在常量池中.
public void test5(){
String s1 = "ab";
String s2 = "cd";
String s3 = "abcd";
String s4 = "ab"+"cd";
String s5 = "ab" + s2;//变量名参与后,就不是在常量池中了
String s6 = s1 + "cd";
String s7 = s1 + s2;
System.out.println(s3==s4);//true
System.out.println(s3==s5);//false
System.out.println(s3==s6);//false
System.out.println(s5==s6);//false
System.out.println(s3==s7);//false
System.out.println(s5==s7);//false
String s8 = s5.intern();//intern()方法:把返回值放回常量池
System.out.println(s3 == s8);//true
}
四、常见方法
操作
++int length()++: 返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty(): 判断是否是空字符串: return value.length == 0
String toLowerCase(): 使用默认语言环境, 将 String 中的所有字符转换为小写(++注意不可变性,要定义一个新变量来存储返回值++)
String toUpperCase(): 使用默认语言环境, 将 String 中的所有字符转换为大写
String trim(): 返回字符串的副本, 忽略前导空白和尾部空白(++把 开头 和 末尾 的空白部分删去++)
boolean equals(Object obj): 比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString): 与equals方法类似, 忽略大小写
String concat(String str): 将指定字符串连接到此字符串的结尾。 等价于用“+”
++int compareTo++(String anotherString): 比较两个字符串的大小(++涉及到字符串排序-首字母排序++)
String substring(int beginIndex): 返回一个新的字符串, 它是此字符串的从beginIndex开始截取到最后的一个子字符串。(删除前 n 个字符)
String substring(int beginIndex, int endIndex) : 返回一个新字符串, 它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。(返回第n 个到第 m 个字符,++注意:是左闭右开区间++)
检索
boolean endsWith(String suffix): 测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始(相当于删去前 n 个字符,在进行测试)
++boolean contains(CharSequence s)++: 当且仅当此字符串包含指定的 char 值序列时,返回 true(检索当前字符串中 是否有 指定字符串 )
int indexOf(String str): 返回指定子字符串在此字符串中第一次出现处的索引(++检索字符串并返回位置–0开始,注意,如果没有找到,返回 -1 ++)
int indexOf(String str, int fromIndex): 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始(相当于删去前 n 个,再开始找)
int lastIndexOf(String str): 返回指定子字符串在此字符串中最右边出现处的索引(从后往前找)
int lastIndexOf(String str, int fromIndex): 返回指定子字符串在此字符串中最后一次出现处的索引,++从指定的索引开始反向搜索++注: indexOf和lastIndexOf方法如果未找到都是返回-1
什么情况下,indexOf() 和 lastIndexOf()的返回值相同
情况一:存在唯一的str, 情况二:没有找到str
- 替换
String replace(char oldChar, char newChar): 返回一个新的字符串, 它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。(都多少个换多少个)
String replace(CharSequence target, CharSequence replacement): 使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement) : 使 用 给 定 的replacement 替换此字符串所有匹配给定的++正则表达式++的子字符串。
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
System.out.println(string);
String replaceFirst(String regex, String replacement) : 使 用 给 定 的replacement 替换此字符串匹配给定的++正则表达式++的++第一个++子字符串。(只替换第一个字符串)
- 匹配
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
String str = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str.matches("\\d+");
System.out.println(matches);
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话
boolean result = tel.matches("0571-\\d{7,8}");
//含义是0571 的后面7或8位是否为数字
System.out.println(result);
- 切片
String[] split(String regex): 根据给定正则表达式的匹配拆分此字符串。
String str = "hello|world|java";
String[] strs = str.split("\\|");
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
System.out.println();
String str2 = "hello.world.java";
String[] strs2 = str2.split("\\.");
for (int i = 0; i < strs2.length; i++) {
System.out.println(strs2[i]);
}
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串, 最多不超过limit个, 如果超过了, 剩下的全部都放到最后一个元素中。
五、String类的使用
1、String类–>基本数据类型、包装类
2、基本数据类型、包装类–>String类
3、String类 --> char[]字符数组
调用String 的 toCharArray()方法
@test
public void test(){
String str = "abc123";
char[] charArray = str1.toCharArray();
}
4、char[]字符数组 --> String类
调用String类的 构造器
@Test
public void test(){
char arr[] = new char[]{'h','e','l','l','o'};
String str = new String(arr);
}
应用:题目:将abcdef变为a++edcb++f(中间4个元素反转)
答案
5、编码:Sting类 --> byte[]字节数组
调用String的getBytes()
@Test
public void test(0{
String str = "abc123中国";
byte[] bytes = str.getbytes();
System.out.print(ArraystoString(bytes));
//[97,98,99,49,50,51,-28,-72,-83,-27,-101,-67]
//UTF-8 三个字节表示一个中文
//调用Arrays数组工具类中的toString()方法
byte[] gbkss = str.getbytes(“gbk”);
//使用gbk字符集进行转化
System.out.print(ArraystoString(gbks));
//[97,98,99,49,50,51,-42,-48,-71,-6]
6、解码:byte[]字节数组–>String类
调用Strign类的构造器
@Test
public void test(0{
String str1 = "abc123中国";
byte[] bytes = str.getbytes();
byte[] gbkss = str.getbytes(“gbk”);
String str2 = new String(bytes);
//使用默认字符集进行解码
System.out.print(str2);
//abc123中国
String str3 = new String(gbks);
//使用gbk字符集进行解码
System.out.print(str3);
//abc123乱码
//出现乱码原因:编码集和解码集不一致
String str4 = new String(gbks,"gbk");
//用指定的解码集解码
System.out.print(str4);
//abc123中国
//没有出现乱码,原因:编码集和解码集一致
六、常见算法题目
1. 模拟一个trim方法,去除字符串两端的空格。
@Test
public void test1(){
String str1 = " hello ";
String str2 = str1.trim();
System.out.println(str2);
}
2. 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
方式一
@Test
public void test2(){
String str1 = "abcdefg";
char arr[] = str1.toCharArray();
//数组的反转(3~6位)
for (int i = 2; i < 4; i++) {
char temp = arr[i];
arr[i] = arr[7-i];
arr[7-i] = temp;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
}
方式二: 使用reverse()方法
方式三:
public String reverse(Stirng str,int startIndex,int endIndex){
StringBuilder builder = new StringBuilder(str.length());
//第一部分
builder.append(str.substring(0,startIndex));
//第二部分
for(int i = endIndex:i >= startIndex;i--){
builder.append(str.charAt(i));
}
//第三部分
builder.append(str.substring(endIndex + 1));
return builder.toString();
}
3. 获取一个字符串在另一个字符串中出现的次数。
比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
方式一
@Test
public void test3(){
String str1 = "abaabbcabab";
String str2 = "ab";
int count = 0;
for (int i = 0; i < str1.length();) {
int sort = str1.indexOf(str2,i);
i = sort + str2.length();
if(sort >= 0){
count++;
}else{
break;
}
}
System.out.println(count);
}
方式二:
public int getCount(String mainStr,String subStr){
int mainLength = nainStr.length();
int subLength = subStr.length();
int count = 0;
int index = 0;
if(mainLength >= subLength){
while((index = mainStr,indexOf(subStr,index)) != -1){
count++;
index +=subLength;
}
return count;
}else{
return 0;
}
}
4.获取两个字符串中最大相同子串。比如:
str1 = "abcwerthelloyuiodef“;str2 = “cvhellobnm”
提示:将短的那个串进行长度依次递减的子串与较长的串比较。
改进,可以用subString()方法
++从长的开始++
方式一:
@Test
public void test4(){
//equals\charAt
String str1 = "abcwerthelloyuiodef";
String str2 = "cvhellobnm";
boolean isFlag = false;//设置标记
for (int i = str2.length(); i >= 1; i--) {
char[] arr = new char[i];
if(isFlag){//跳出循环
break;
}
for (int k = 0; k <= str2.length() - i; k++) {
for (int j = 0,l = k ; j <= i-1 ; j++,l++) {//转换为数组
arr[j] = str2.charAt(l);
}
String str3 = new String(arr);//转回字符串
int ret = str1.indexOf(str3);//对比
if(ret >= 0){
System.out.println(str3);
isFlag = true;//标记
break;
}
}
}
}
方式二:
可以在调用时加一个判断,提高程序的健壮性
public String getMaxSameString(String str1,String str2){
String maxStr = (str1.length() >= str2.length())?str1:str2;
String minStr = (str1.length() <= str2.length())?str1:str2;
int length = minStr.length();
for(int i = 0;i < lengthl;i++){
for(int x = 0,y = length - i;y <= length:x++,y++){
String subStr = minStr.substring(x,y);
if(maxStr.contains(minStr)){
return subStr;
}
}
}
return null
}
5.对字符串中字符进行自然顺序排序。
提示:
1)字符串变成字符数组。
2)对数组排序,选择,冒泡, Arrays.sort();
3)将排序后的数组变成字符串。
@Test
public void test5(){
String str = "qwertyuiomQWERTYUIXCVBNM";
char[] arr1 = str.toCharArray();
Arrays.sort(arr1);
String str2 = new String(arr1);
System.out.println(str2);
}
六、另外两个有关的类–StringBuffer\StringBuilder
StringBuffer和StringBuillder的使用
++1、三者的异同:++
异:
String:不可变的字符序列、char[]数组长度不固定
StringBuffer:可变的字符序列、线程安全的、效率低、char[]数组长度固定–16个字符
StringBuilder(JDK 5.0 新增):可变的字符序列、线程不安全的、效率高(++非多线程时,推荐使用++)、char[]数组长度固定–16个字符
同:
1、底层结构使用 char[]数组存储
2、三者字符串长度都是元素个数(StringBuffer和StringBuilder重写了length()方法)
2、面试题:扩容问题:如果添加的数组底层数组放不下,那就需要扩容底层数组
默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组
指导意义:在开发中建议使用:StringBuffer(int capacity),即构造器,避免扩容
StringBuffer中比String中多的方法
StringBuffer append(xxx):提供了很多的append()方法, 用于进行字符串拼接
- 使用:方法链条:s1.aplend().aplend().aplend()
@Test
public void test(){
StringBuffer s1 =new StringBuffer("abc");
s1.append(1);
s1.append("1");
StringBuffer delete(int start,int end):删除指定位置的内容 (++注意左闭右开区间++)
@Test
public void test(){
StringBuffer s1 =new StringBuffer("abc");
s1.append(1);
s1.append("1");
s1.delete(2,4);
System.out.println(s1);//ab1
//不用接收返回值
//直接在数组本身上更改(可变性)
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end)返回一个从[start,end)的子字符串(++注意:要接收返回值++)
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)
总结:关注
- 增: append(xxx)
- 删: delete(int start,int end)
- 改: setCharAt(int n ,char ch)/replace(int start, int end, String str)
- 查: char charAt(int n )/indexOf(String str)
- 插: insert(int offset, xxx)
- 长度: length()
- 遍历:for()+charAt()/toString()
七、三者效率对比测试
三者效率从高到低排列
StringBuilder > StringBuffer > String
@Test
public void test(){
// long 初始设置 startTime = 0L; 三者的效率测试
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间: " + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间: " + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间: " + (endTime - startTime));
}