共计 4862 个字符,预计需要花费 13 分钟才能阅读完成。
String.intern()方法
首先判断这个常量是否存在于常量池。
- 如果存在,则直接返回地址值(只不过地址值分为两种情况,1是堆中的引用,2是本身常量池的地址)
- 如果是引用,返回引用地址指向的堆空间对象地址值
- 如果是常量,则直接返回常量池常量的地址值,
- 如果不存在,将当前对象引用复制到常量池,并且返回的是当前对象的引用
- 案例
public static void main(String[] args){
String s1 = new String("1")+new String("23");
s1.intern();
String s2 = "123";
System.out.println( s1 == s2);
}
- 分析:
- 第一行是两个
new String
类型的字符串相加,创建了堆中有3个对象 一个是1
, 一个是23
,还有一个是结果123
(在堆中),由于程序刚启动常量池也没有 1,23 所以会在常量池创建2个对象 (1
, 23) - 当
s1
执行intern()
方法之后,首先去常量池判断有没有123,此时发现没有,所以会把对象加入到常量池(堆中的地址),并且返回当前对象的引用(堆中的地址) - 当创建s2时候,并且找到常量池中123,并且把常量池的地址值返回给s2(常量池的地址其实就是堆中的地址)
- 由于常量池的地址值就是s1调用
intern()
方法之后得到的堆中的引用,所以此时s1
和s2
的地址值一样,输出true
。
- 案例
public static void main(String[] args){
String s1 = new String("1")+new String("23");
String s2 = "123";
s1.intern();
System.out.println( s1 == s2);
}
分析
- 在执行到第二行的时候
String s2 = "123"
时,发现常量池没有123
,所以会在常量池中先创建一个常量(此时的常量地址不是堆中的地址,就是是在的常量池地址) - 在当
s1
调用intern()
方法时,会发现常量池已经有了123
对象,就会直接把123
的常量(地址)给返回出去,但是由于返回值并没有接收,所以此时s1
还是堆中地址,则输出false
; - 如果代码换成
s1 = s1.intern();
那s1就会重新指向常量池了,那输出就为true
;
- 练习
String s = new String("1");
String s2 = "1";
s.intern();
//false
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
//false
System.out.println(s3 == s4);
String s = new String("1");
String s2 = "1";
s=s.intern();
//true
System.out.println(s == s2);
String str1 = new String("SEU") + new String("Calvin");
System.out.println(str1.intern() == str1); //true
// str1.intern()发现常量池中不存在“SEUCalvin”,
//因此在常量池中创建"SEUCalvin",也就直接指向了堆中的str1了。
System.out.println(str1 == "SEUCalvin"); //true
String str2 = "SEUCalvin";//新加的一行代码,其余不变
String str1 = new String("SEU")+ new String("Calvin");
System.out.println(str1.intern() == str1); //false
System.out.println(str1 == "SEUCalvin"); //false
- 案例
String s1 = "23";
/*
* 这里执行时,常量“1” 会首先到字符串常量池里面去找,如果没有就创建一个,
* 并且加入字符串常量池。
* 得到的123结果对象,不会存入到常量池。这里特别注意和两个常量字符串相加不同 “1”+“23”
* 由于不会进入常量池,所以s2 和 s3 常量池地址值不同,所以输出为false
*/
String s2 = "1" + s1;
String s3 = s2.intern();
String s4 = "123";
System.out.println(s3 == s4); //true
System.out.println(s3 == s2); //true
创建了几个对象
String s1 = new String("123")
创建了几个对象,String s2 = "123"
创建 了几个对象
直接写双引号常量来创建
判断这个常量是否存在于常量池,
- 如果存在,则直接返回地址值(只不过地址值分为两种情况,1是堆中的引用,2是本身常量池的地址)
- 如果是引用,返回引用地址指向的堆空间对象地址值
- 如果是常量,则直接返回常量池常量的地址值,
- 如果不存在,在常量池中创建该常量,并返回此常量的地址值
- 案例
String s = "123";
//true,因为s已经在常量池里面了,s.intern()返回的也是常量池的地址,两者地址一样为true
System.out.println(s == s.intern());
new String创建字符串
首先在堆上创建对象(无论堆上是否存在相同字面量的对象),然后判断常量池上是否存在字符串的字面量(字符串的值)
- 如果不存在则在常量池上创建常量(并将常量地址值返回)
- 如果存在不做任何操作
- 案例
String s = new String("123");
/*
严格来说首先肯定会在堆中创建一个123的对象,然后再去判断常量池中是否存在123的对象,
如果不存在,则在常量池中创建一个123的常量(与堆中的123不是一个对象),
如果存在,则不做任何操作。
因为常量池中是有123的对象的,s指向的是堆内存中的地址值,
s.intern()返回是常量池中的123的常量池地址,所以输出false
*/
String s2 = s.intern();
System.out.println(s == s.intern());
两个双引号的字符串相加
判断这两个常量、相加后的常量在常量池上是否存在
- 如果不存在则在常量池上创建相应的常量(并将常量地址值返回)
- 如果存在,则直接返回地址值(只不过地址值分为两种情况,1是堆中的引用,2是本身常量池的地址)
- 如果是引用,返回引用地址指向的堆空间对象地址值
- 如果是常量,则直接返回常量池常量的地址值
String s = "Hello,World";
String str = "Hello" + "," + "World";
String s1 = new String("123").intern();
String s2 = "1" + "23";
/*
* String s1 = new String("123") 创建了两个对象(一个堆中,一个常量池中)
此时s1指向堆中
* 当s1调用.intern()方法之后,发现常量池中已经有了字面量是123的常量,
则在常量池中创建一个对象,地址是堆内存地址,并返回给s1
* 在执行s2等于123时候,去常量池查看,同上常量池已经存在了,
则此时s2不创建对象,直接拿常量池123的地址值使用
* 所以此时s1 和 s2 都代表是常量池的地址值,则输出为true
*/
System.out.println(s1 == s2);
System.out.println(s == str);
两个new String()的字符串相加
首先会创建这两个对象(堆中)以及相加后的对象(堆中)然后判断常量池中是否存在这两个对象的字面量常量
- 如果存在,不做任何操作
- 如果不存在,则在常量池上创建对应常量
String s1 = new String("1")+new String("23");
/*
* 首先堆中会有 1 ,23 ,以及相加之后的123 这三个对象。
如果 1,23 这两个对象在常量池中没有相等的字面量
* 那么还会在常量池中创建2个对象 最大创建了5个对象。最小创建了3个对象都在堆中。
*/
s1.intern();
String s2 = "123";
System.out.println( s1 == s2);// true
双引号字符串常量与new String字符串相加
首先创建两个对象,一个是new String
的对象(堆中),一个是相加后的对象(堆中),然后判断双引号字符串字面量和new String
的字面量在常量池是否存在
- 如果存在,不做操作
- 如果不存在,则在常量池上创建对象的常量
- 案例
String s1 = "1"+new String("23");
/*
*首先堆中会有 23 ,以及相加之后的123 这2个对象
如果23,1 这两个对象在常量池中没有相等的字面量,那么还会在常量池中创建2个对象最多创建了4个对象(2个堆中,2个在常量池中)。最少创建了2个对象都堆中。
*/
String s2 = "123";
System.out.println( s1 == s2);// false
- 分析
如果换成System.out.println( s1.intern() == s2);
则返回的是true
双引号字符串常量与一个字符串变量相加
首先创建一个对象,是相加后的结果对象(存放堆中,不会找常量池),然后判断双引号字符串字面量在常量池是否存在
- 如果存在,不做操作
- 如果不存在,则在常量池上创建对象的常量
String s1 = "23";
/*
* 这里执行时,常量“1” 会首先到字符串常量池里面去找,如果没有就创建一个,并且加入字符串常量池。
* 得到的123结果对象,不会存入到常量池。这里特别注意和两个常量字符串相加不同于 “1”+“23”
* 由于不会进入常量池,所以s2 和 s3 常量池地址值不同,所以输出为false
*/
String s2 = "1"+s1;
String s3 = "123";
System.out.println( s2 == s3.intern());
- 分析
如果打印System.out.println( s2.intern() == s3.intern());
则返回结果为true
,因为s2.intern()
回去常量池中找有没有123
常量,发现有则直接返回常量地址,而123
又是常量池地址(非堆引用地址)
正文完