String s = new String(“hello“)创建了几个对象(图解)

String#intern()的作用是:运行期,将字符串动态加入字符串常量池,实现字符串对象复用,减小内存开销。

  • jdk1.6及之前,由于字符串常量池在永久代(方法区)中,和堆区不在同一内存区域。此时调用String#intern() 会先通过equals(key)查看字符串常量池中是否有该字符串存在,有则直接返回该字符串对象的引用,没有则将堆中的字符串复制一份,放到字符串常量池中,并保存复制到字符串常量池中的这个字符串对象的引用到hash表中,然后返回该字符串对象的引用。

  • jdk1.7及之后,字符串常量池被移至堆区的新生代中,此时调用String#intern()后,查找字符串常量池发现没有该字符串对象时,不再是将堆中的字符串对象赋值一份到字符串常量池中,而是将堆中的字符串对象的引用保存到hash表中,然后返回该字符串对象的引用。


String s = new String(“hello”);创建了几个对象

1个 或 2个

String s = new String("hello");  这段代码创建了几个字符串对象?


3个 或 4个 或 5个

String s = new String("he") + new String("llo");  这段代码创建了几个字符串对象?


1个

StringBuilder类的toString(), 底层是动态创建new String("hello"), 那么此时的new String(object)创建了几个字符串对象?


与jdk版本有关:(jdk1.6及之前是:3个 或 4个 或 5个 或 6个),(jdk1.7及之后是:3个 或 4个 或 5个)

String s = new String("he") + new String("llo");  这段代码创建了几个字符串对象?
String s1 = s.intern();



字符串常量拼接

对于字符串常量的拼接操作,编译期间编译器会对其进行优化。

public static void main(String[] args){
     String s1 = "he" + "llo"; //编译期会进行优化:等价与String s1 = "hello";
     String s2= "hello";
     System.out.println(s1==s2); //true
     System.out.println(s1.equals(s2)); //true
}


对于字符串实例引用的拼接,编译器无法对其进行优化。在底层会调用new StringBuilder().append().toString()来生成对象引用。 并且 StringBuilder 类的 toString() 方法会生成新的String对象,所以二者不相等

public static void main(String[] args){
     String s0 = "he" + "llo";  //编译后会优化成hello: 等价于String s0 = "hello";

     String s1 = "he";
     String s2 = "llo";
     //s3不会被编译器优化,因为s1、s2是变量,后期是可能会发生变化的,所以编译器无法对他们进行优化。而是使用StringBuilder拼接:等价于String s3 = new StringBuilder().append(s1).append(s2).toString();
     String s3 = s1 + s2;
     System.out.println(s0 == s3); //false
     
	 String ss1 = "he";
     //ss3不会被编译器优化,因为ss1是变量,后期是可能会发生变化的,所以编译器无法对他们进行优化。而是使用StringBuilder拼接:等价于String ss3 = new StringBuilder().append(ss1).append("llo").toString();
     String ss3 = ss1 + "llo";
     System.out.println(s0 == ss3); //false		
}
//StringBuilder类中toString()方法的源码
public String toString() {
     return new String(value, 0, count);
}


final修饰的字符串,再编译器也会进行优化

public static void main(String[] args) {
     String s0 = "hello";
     final String s1 = "he";
     final String s2 = "llo";
     String s3 = s1 + s2;  //编译后会优化成hello: 等价于String s3 = "hello";
     System.out.println(s0 == s3);  //true
}
public static void main(String[] args) {
     String s0 = "hello";
     final String s1 = "he";
     String s2 = s1 + "llo";  //编译后会优化成hello: 等价于String s2 = "hello";
     System.out.println(s0 == s2);  //true
}



0、判断输出
public static void main(String[] args) {
     String s1 = "hello";
     s1 = "world";
     System.out.println(s1 == s2);  //false
}

String s1 = “hello”;   //
蔡栋
s1 = “world”;   // String 底层被final修饰,已经创建就不允许被修改。这里表面是被修改了,实则是创建了一个新的字符串对象,并将新字符串对象的引用指向s1
蔡栋



1、判断输出
public static void main(String[] args) {
    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2);  //true
}

String s1 = “hello”;   //
蔡栋
String s2 = “hello”;   // 字面量方式创建字符串对象前,会先去检索字符串常量池,判断是否存有该字符串对象的引用。有则直接返回该引用。没有则在字符串常量池中创建一个字符串对象,并将该对象的引用保存到hash表中,然后再返回该对象的引用。
蔡栋



2、判断输出
public static void main(String[] args) {
     String s1 = new String("hello");
     String s2 = new String("hello");
     System.out.println(s1 == s2);  //false
}

String s1 = new String(“hello”);   //
蔡栋

String s2 = new String(“hello”);   //
蔡栋



3、判断输出
public static void main(String[] args) {
     String s1 = new String("hello");
     String s2 = "hello";
     System.out.println(s1 == s2);  //false
}

String s1 = new String(“hello”);   //
蔡栋

String s2 = “hello”;   //
蔡栋



4、判断输出
public static void main(String[] args) {
     String s1 = new String("hello");
     s1.intern();
     String s2 = "hello";
     System.out.println(s1 == s2);  //false
}

String s1 = new String(“hello”);   //
蔡栋

s1.intern();   // 由于没有定义变量来接收String#intern() 返回的引用,所以这里就不画图了

String s2 = “hello”;   //
蔡栋



5、判断输出
public static void main(String[] args) {
     String s1 = new String("hello");
     String s2 = s1.intern();
     String s3 = "hello";
     System.out.println(s1 == s2);  //false
     System.out.println(s2 == s3);  //true
}

String s1 = new String(“hello”);   //
蔡栋

String s2 = s1.intern();   // StringTable中有保存hello字符串的对象引用,所以直接返回该对象引用
蔡栋
String s3 = “hello”;   //
蔡栋



6、判断输出(String#intern()- - > jdk1.8)
public static void main(String[] args) {
     String s1 = new String("he") + new String("llo");
     s1.intern();
     String s2 = "hello";
     System.out.println(s1 == s2);  //true
}

String s1 = new String(“he”) + new String(“llo”);   // 会创建5个字符串对象(也可能是3个或4个)。在字符串常量池通过字面量创建"he" 和 “llo"字符串对象,并保存其对象引用,以及在堆中创建"he” 和 “llo"字符串对象,另外底层是通过new StringBuilder().append(“he”).append(“llo”).toString()进行拼接。其中StringBuilder的toString()会生成一个新的字符串对象new String(“hello”),但由于这个代码是运行期动态生成的,所以不会在字符串常量池中创建字符串对象"hello”,但会在堆中创建字符串对象"hello"
蔡栋

s1.intern();   // 在jdk1.7及以后,intern()方法会先去查看StringTable中是否已经有该字符串对象引用存在。没有则直接将在堆中创建的字符串对象的引用存到StringTable中 (jdk1.6之前,是直接则将堆中的字符串对象复制一份,放到方法区的字符串常量池中,并保存复制到字符串常量池中的这个字符串对象的引用到hash表中,然后返回该字符串对象的引用)。有则直接返回字符串常量池中的引用
蔡栋

String s2 = “hello”;   // 通过字符串的hash查找时,发现hash表中有该字符串对象引用存在,所以直接返回对象引用给变量s2。没有则再字符串常量池中创建一个字符串对象,然后将其引用保存到hash表中,然后返回该字符串对象引用给变量s2
蔡栋


6、判断输出(String#intern()- - > jdk1.6)
public static void main(String[] args) {
     String s1 = new String("he") + new String("llo");
     s1.intern();
     String s2 = "hello";
     System.out.println(s1 == s2);  //false
}

String s1 = new String(“he”) + new String(“llo”);
蔡栋

s1.intern();   // jdk1.8中调用intern()方法,会通过字符串的hash值查找StringTable中是否保存了该字符串的引用,有则直接返回该引用,没有则将堆中该字符串对象的引用存到StringTable中,然后再返回改该引用。 // jdk1.6则是判断StringTable中是否保存了该字符串的引用,有则直接返回该引用,没有则将堆中该字符串对象赋值一份到字符串常量池中,然后将复制的这份字符串对象的引用存到StringTable中,然后再返回该引用。

初学者注意啦图中的"he" 字符串对象 和 new String(“he”)字符串对象只是为了让读者容易理解才这样表示而已,它们都是字符串对象,内部存储结构都是一样的。 // 所以别有:new String(“hello”)创建的字符串对象不是都放在堆中吗,怎么还能放到字符串常量池中啊,这样的错误理解。new创建的字符串对象确实是只能放在堆中,但这里我只是把这个字符串对象用new String(“hello”)形式表示而已,别把它理解成程序中的new String(“hello”)了
蔡栋

String s2 = “hello”;   // 通过字符串的hash查找时,发现hash表中有该字符串对象引用存在,所以直接返回对象引用给变量s2。没有则再字符串常量池中创建一个字符串对象,然后将其引用保存到hash表中,然后返回该字符串对象引用给变量s2
蔡栋


7、判断输出(String#intern()- - > jdk1.8)
public static void main(String[] args) {
     String s1 = new String("he") + new String("llo");
     String s2 = s1.intern();
     String s3 = "hello";
     System.out.println(s1 == s2);  //true
     System.out.println(s1 == s3);  //true
     System.out.println(s2 == s3);  //true
}

String s1 = new String(“he”) + new String(“llo”);
蔡栋
String s2 = s1.intern();
蔡栋
String s3 = “hello”;
蔡栋


7、判断输出(String#intern()- - > jdk1.6)
public static void main(String[] args) {
     String s1 = new String("he") + new String("llo");
     String s2 = s1.intern();
     String s3 = "hello";
     System.out.println(s1 == s2);  //false
     System.out.println(s1 == s3);  //false
     System.out.println(s2 == s3);  //true
}

String s1 = new String(“he”) + new String(“llo”);
蔡栋

String s2 = s1.intern();   // 因为跨内存区域调用是不推荐的。所以jdk1.6之前String#intern()采用复制的方式。直到jdk1.7之后,字符串常量池被移至堆中,改为存引用方式
蔡栋

String s3 = “hello”;
蔡栋



8、判断输出(String#intern()- - > jdk1.8)
public static void main(String[] args) {
     String s1 = "hello";
     String s2 = new String("he") + new String("llo");
     String s3 = s2.intern();
     System.out.println(s1 == s2);  //false
     System.out.println(s1 == s3);  //true
     System.out.println(s2 == s3);  //false
}

String s1 = “hello”;   //
蔡栋

String s2 = new String(“he”) + new String(“llo”);   //
蔡栋

String s3 = s2.intern();   // String#intern() 会先去字符串常量池,通过字符串的hash查找,发现hash表中有该字符串对象引用存在,所以直接返回该引用
蔡栋

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家师曹先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值