String类是final类,不能被继承
String类有属性private final char value[ ]用与存放字符串的内容(这个字符数组是final类型,不可修改(这个不可修改指的是value指向的地址),所以,String是最后都是指向常量池,而更换值,也最后是在常量池中操作)
public class test {
public static void main(String[] args) {
final char [] one = {'a','b','c'};
char [] two = {'d','e'};
one = two;//报错
//如果有final,不能这样更改引用类型的地址
}
}
先查看常量池里面是否有这个字符串常量,如果有,直接指向,没有,创建后再指向
先在堆中创建空间,看里面的value数组有没有指向我们需要的字符串常量,如果有value就直接指向,没有创建后value再指向,最后把value存在的堆空间地址返回
分析:因为在创建a的时候,常量池有这个数据了,所以创建b的时候直接指向该常量池,所以地址相等
intern方法是返回一个常量池的地址,首先它会查看在常量池有没有和调用该方法的字符串相同的字符串,如果有,返回常量池中该字符串的地址,没有,创建后返回常量池中的地址
分析:
1.字符串内容相同,T
2.a是来自于常量池,b是堆空间, F
3.a和b.intern都是常量池中的同一个对象,T
4.b是堆空间地址,b,intern是常量池的一个地址,F
分析:
1.比较内容,所以是相等,T
2.在创建两个对象时,他们的name都是指向的常量池,又因为内容相同,所以是常量池的同一个地址,T
3.name本身就是指向的常量池,T
public class test {
public static void main(String[] args) {
person p1 = new person();
person p2 = new person();
System.out.println(p1.name == p2.name);
//false,因为下面那种创建方式,是两个不同的堆空间的地址
}
}
class person{
String name = new String("sad");
}
这种String的创建方式,会让name保存的是创建一个新的堆空间的地址,而这个堆空间,都指向常量池,但是name保存的堆空间地址不同
两个
分析:在常量池本来的对象不会更改,而是重新创建一个,让该引用指向它
判断是否有引用的指向,没有就不会在常量池单独创建
分析:
这里是含有字符串变量的拼接,看源码会发现,是先在底层创建了一个StringBuilder的对象,然后调用append方法,最后通过toString方法返回,而toString方法返回是新建一个String对象,所以这就是前面的第二种创建方式,会返回一个堆空间地址,在堆空间里面指向常量池
public class test {
public static void main(String[] args) {
String c = "abc" + "efg";
//这是直接在常量池里面创建的
String a = "abc";
String b = "efg";
String e = a + b;
//这是在底层调用方法,最后是以创建新String对象返回
System.out.println(c == e);
//因为c在常量池,而e在堆空间,所以false
}
}
如果是字符串常量的拼接**(在常量池),就是会在常量池创建,而如果含有字符串的引用变量(在堆空间)**,那么就会在底层调用方法,最后返回一个String对象,而存储的是堆空间地址
最后会输出:hspand hava
分析:在调用方法时传入的str也指向ex对象中的那个str的堆空间,但是根据String的赋值,对于我们传入的str不会指向那个堆空间,而是重新在常量池匹配,有相同内容的字符串就指向,没有就创建后再指向,而在调用完方法后,我们的形参就会销毁掉,所以最后并没有实际对ex对象里的str造成影响
效率低原因:
因为在进行拼接时,底层每次会进行开辟新空间,赋值后,再返回。
对于compareTo,
每个字符比较
1)字符和长度完全相等返回0
2)字符相同,长度不同时,返回 a.length-b.length
3)字符不同时,返回(ASCII码值)第一次不同字母时的ASCII码差值
对于substring
提示:这里第一种,是从第6个索引开始截取后面的内容
第二种,是从前面那个索引开始截取到后面那个索引
(包首不包尾)
对于format
要记住这里的占位符和其对应的类型
其他具体语法查API
为什么说StringBuffer不是每次更新?
它是在这个字符数组达到一定的限度,再进行一次扩容,而String每次导致常量池创建新对象
与String的区别,String的value是final修饰的,但是这个没有,所以它是堆空间指向了一个字符数组(也在堆空间),而在这个字符数组里面存储字符串,就不需要常量池(如下)
方法一:直接通过构造器
public static void main(String[] args) {
String str = "sad";
StringBuffer buffer = new StringBuffer(str);
}
方法二:
通过StringBuffer的append方法
public static void main(String[] args) {
String str = "sad";
StringBuffer buffer = new StringBuffer();
buffer = buffer.append(str);
}
方法一:
通过调用StringBuffer的tostring方法
方法二:直接使用构造器
public static void main(String[] args) {
StringBuffer buffer = new StringBuffer("sad");
//方法一:调用toString
String str1 = buffer.toString();
//方法二:直接构造器
String str2 = new String(buffer);
}
查看源码,发现append把null转换为了一个字符串,而下面的构造器直接传入,会报空指针异常
StringBuilder相对于StringBuffer来说使用是一模一样的,在单线程使用StringBuilder效率更高,但其不适合在多线程使用
分别从效率,线程还有字符序列来进行说明
效率:StringBuilder最高,StringBuffer其次,最后String
线程:StringBuffer线程安全,StringBuilder线程不安全
字符序列:只有String不可以更改字符序列
在要对String进行大量更改时,一般不使用String