虽然我们经常使用到String,但其实对于里面的特性还是很多没有被发现的。
String的实例化方式:
1.通过字面量定义
2.通过new+构造器的方式
String:字符串,使用一对“”引起来表示
1.String声明为final的,不可被继承
2.String实现了Serializable(可序列化)接口:表示字符串支持序列化
实现了Comparable接口:表示String可以比较大小
3.String内部定义了final char[] value用于存储字符串数据
4.String:代表不可变的字符序列,简称:不可变性
体现:1.当对字符串重新赋值时,需要重写指定内存区域赋值不能使用原有的value进行赋值(在对s1重新赋值中有所体现)
2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值不能使用原有的value进行赋值(在对s3=s2+“def”两者字面量不同中有所体现)
3.调用replace()方法时也类似。
5.通过字面量的方式(区别于new方法)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的
4,5,6下面给出解析:
字面量的定义方式:String s1 = “abc”;
String s2 = “abc”;
此时s1与s2指向同一个地址
因为在方法区中:不会出现字面量相同的两个地址值,因此在给s2赋值时,会先观察方法区中是否有对应的值,若有,直接把地址给s2,若没有,再新创
若s1=“hello",由于String的不可变性,因此会在方法区中重新定义”hello“并使s1拿到它的地址
new就相当于在堆中新建了value值,那么每new一个对象就会在堆中新建,地址值也因此不同,堆中的value存储着指向常量池的引用地址
面试题:
String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
2个,一个是堆空间中new的对象,另一个是char【】对应的常量池中的数据:“abc”
结论:只要有变量参与的就地址就指向堆中的数据
涉及到了些JVM的知识没有记录,后续再专门学习JVM吧。
下面列举一些String的常用方法,
也可在菜鸟文档进行搜索
[Java String 类 | 菜鸟教程 (runoob.com)
String与基本数据类型转换:
UTF-8:用三个二进制数据存储汉字
gbk:用两个二进制数据存储汉字
算法题:
public class StringTest1 {//模拟一个trim方法,去除字符串两端的空格
public static void main(String[] args) {
String str = " addadasd ";
StringTest1 s = new StringTest1();
String s1 = s.trim1(str);
System.out.println(s1);
}
public String trim1(String str){
int start = 0;
int end = str.length()-1;
while(start<=end&&str.charAt(start)==' '){
start++;
}
while(start<=end&&str.charAt(end)==' '){
end--;
}
return str.substring(start,end+1);
}
}
public class StringTest2 {//将一个字符串进行反转,将字符串中指定部分进行反转,比如“abcdefg”反转为“abfedcg”
public static void main(String[] args) {
StringTest2 stringTest2 = new StringTest2();
String str = "abcdefg";
String reverse = stringTest2.reverse(str, 3, 6);
System.out.println(reverse);
}
public String reverse(String str,int start,int end){//start表示需要反转的起始位置,end表示末尾位置
String reverse = "";
String reverse1 = "";
String reverse2 = "";
char []array = str.toCharArray();
for(int i = end-1;i>start-2;i--){
reverse+=array[i];
}
for(int i = 0;i<start-1;i++){
reverse1+=array[i];
}
for(int i = end;i<array.length;i++){
reverse2+=array[i];
}
String s = reverse1 + reverse + reverse2;
return s;
}
}
//获取一个字符串在另一个字符串中出现的次数
//比如:ab在abkkcadkabkebfbabkskab出现的次数
public class StringTest3 {
public static void main(String[] args) {
String str1 = "abkkcadkabkebfbabkskab";
String str2 = "ab";
StringTest3 s = new StringTest3();
int sum = s.fench(str1,str2);
System.out.println(sum);
}
public int fench(String str1,String str2){
int sum = 0;
int index = 0;
if(str1.length()>str2.length()){
while((index=str1.indexOf(str2,index))!=-1){
sum++;
index+=str2.length();
}
return sum;
}else{
return 0;
}
}
}
//获取两个字符串最大相同子串
public class StringTest4 {
public static void main(String[] args) {
StringTest4 stringTest4 = new StringTest4();
String st = stringTest4.st("abcd", "abc");
System.out.println(st);
}
public String st(String str1,String str2){
int a = str1.length();
int b = str2.length();
if(str1.length()<=str2.length()){
int temp = a;
a = b;
b=temp;
}
for(int x = 0;x<b;x++){
for(int y =0,z=b-x;z!=b+1;y++,z++){
String temp = str2.substring(y,z);
return temp;
}
}return null;
}
}
StringBuffer,StringBuilder,String三者的异同?
效率从高到低:StringBuilder,StringBuffer,String
String:不可变的字符序列,底层使用char[]存储
StringBuffer:可变的字符序列,效率安全,效率低
StringBuilder:JDK5.0新增的,可变的字符序列,线程不安全,效率高
源码分析:
String str = new String();//char[]value=new char[0];
String str1 = new String("abc");//new char[]{'a','b','b'};
StringBuffer sb1= new StringBuffer();//char[]value=new char[16];底层创建了一个长度是16的数组
sb1.append('a')//value[0]='a';
sb1.append('b');
StringBuffer sb2= new StringBuffer("abc");//char[]value=new char["abc".length+16];
问题1:输出sb2.length()返回3
问题2:扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组?
默认情况下:扩容为原来容量的2倍+2,同时将原有数组的元素复制到新的数组中。
指导意义:开发中建议StringBuffer(int capacity)或StringBuilder(int capacity)
StringBuffer,StringBuilder类的常用方法
增:append(xxx)
删:delete(int start,int end)
改:replace(int start,int end,String str)/setCharAt(int n,char ch)
查:charAt(int n)
插:insert(int offset,xxx)
长度:length()
遍历:for()+charAt() / toString()