Java基础汇总(二)——String(重点string.intern())

本文解析了Java中String对象的==和equals区别,强调了String的不可变性,比较了String、StringBuffer和StringBuilder的差异,重点讲解了String.intern()的作用。通过实例说明了字符串常量池和堆的操作原理。
摘要由CSDN通过智能技术生成

 一、==运算符和equals之间的区别:

  • 对于基本数据类型==比较的是值是否相同
  • 对于引用数据类型==比较的是内存地址是否相同
  • equals比较引用指向的内容(值)是否相同

二、字符串的不可变性

String的对象一旦被创建,则不能修改,是不可变的。所谓的修改其实是创建了新的对象,引用指向新的对象。

例:

String s = "abc";// (1) 创建新的对象"abc" s为指向对象abc的引用
System.out.println("s = " + s);//abc
s = "123";  (2) //创建新的对象"123" 引用s指向新的对象123
System.out.println("s = " + s);//123

执行(1)处代码后,首先会在方法区的运行时常量池创建一个新的对象"abc",其次在Java栈中创建一个对象的引用s,并让s指向"abc"

执行(2)处代码后,首先会在方法区的运行时常量池创建一个新的对象"123",然后让Java栈中创建的的引用s重新指向“123”,而原来的对象"abc"还在内存中,并没有改变。

1. String为什么不可变

  • String类用final关键字修饰,不可继承
  • 但是String对象的引用本身是可以改变指向其他的对象的
  • final修饰的char[]数组保证了char数组的引用不可变,内容可变。但String内部并不提供方法来完成这一操作,所以String的不可变是基于代码封装和访问控制的。

例:java.lang.String类代码前三行

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {   
  /** String本质是个char数组. 而且用final关键字修饰.*/     
private final char value[];
 } 

String不可变是为了线程的安全!!!

2. String, StringBuffer and StringBuilder的区别:

可变性:

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

线程安全:

  • String 不可变,因此是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步
  • StringBuilder 不是线程安全的

适用场景:

  • 少量的字符串数据,可以考虑String
  • 单线程场景下操作大量字符串数据,考虑使用StringBuilder
  • 多线程场景下操作大量字符串数据,考虑使用StringBuffer 

3. StringBuffer

StringBuffer提供appendadd方法,可以将字符串添加到已有序列的末尾或指定位置

  • append方法
String s = "abc";
String s1 = s + s;
System.out.println("s1 = " + s1);

String类型在使用 “+” 运算符时,首先把“abc”封装成stringbuilder,其次调用append方法,最后再用tostring返回。所以当大量使用字符串加法时,会大量地生成stringbuilder实例,这是十分浪费的,这种时候应该用stringbuilder来代替string。

三、String.intern()

例1:

String s = new String("1"); 

/* JDK1.6及以下:首先在字符串常量池和堆中创建相应对象"1",s为指向堆中的引用
   JDK1.7及以上:首先在字符串常量池和堆中创建相应对象"1",s为指向堆中的引用
/*

s.intern();

/* JVM判断字符串常量池中是否存在对象"1",若存在,引用s依旧指向堆中;
   若不存在
   JDK1.6及以下:将堆中相应对象复制到字符串常量池中,引用s.intern()指向常量池
   JDK1.7及以上:将堆相应对象的地址放到字符串常量池中,引用s.intern()指向常量池
/*

String s2 = "1";

/* JVM判断字符串常量池是否存在对象"1",
   若存在,引用s2直接指向常量池(JDK1.6及以下:引用s2指向常量池中的对象"1";
                               JDK1.7及以上:引用s2指向常量池中对象"1"在堆中的地址,相当于引用s2指向堆);
若不存在,在常量池中创建相应对象"1",引用s指向相应常量池
/*

System.out.println(s == s2); // 无论JDK1.6及以下还是JDK1.7及以上输出结果为false


例2:

String s3 = new String("1") + new String("1");

/* JDK1.6及以下:首先在字符串常量池中创建对象"1",其次在堆中分别创建两个String对象"1",最后在堆中通过.append方法创建对象"11",引用s3指向对象"11"
   JDK1.7及以上:首先在字符串常量池中创建对象"1",其次在堆中分别创建两个String对象"1",最后在堆中通过.append方法创建对象"11",引用s3指向对象"11"
/*

s3.intern();

/* JVM判断字符串常量池中是否存在对象"11",若存在,引用s3依旧指向堆中;
   若不存在
   JDK1.6及以下:将堆中相应对象复制到字符串常量池中,引用s3.intern()指向常量池
   JDK1.7及以上:将堆相应对象的地址放到字符串常量池中,引用s3.intern()指向常量池(这时的引用s3相当于指向堆)
/*

String s4 = "11";

/* JVM判断字符串常量池是否存在对象"11",
   若存在,引用s2直接指向常量池(JDK1.6及以下:引用s4指向常量池中的对象"11";
                               JDK1.7及以上:引用s4指向常量池中对象"11"在堆中的地址,相当于引用s4指向堆);
若不存在,在常量池中创建相应对象"11",引用s4指向相应常量池
/*

System.out.println(s3 == s4);

/* JDK1.6及以下 false
   JDK1.7及以上 true
/* 

图1:JDK1.6和1.7的例1都为图1所示

1b583588bdd0b324122d69f10af1947c.png

 图2:JDK1.6为黑色线条 所示;JDK1.7为红色线条所示

3a9d8faa20fbeb496aa326256827895b.png

 搞懂String.intern()的关键就是对于new String(),其会分别在常量池和堆中分别建立新的对象引用,然后将引用指向堆中;然后s.intern()会将堆中的内容或者地址放置在常量池中(若常量池中不存在该对象),反之引用不变;而常量字符串的赋值如String s2 = "1"直接放入常量池中;

对于如new String(“1”)+new String(“1”)的字符串,其首先在常量池中创建对象“1”,其次在堆中创建两个new String(“1”),最后通过.append方法在堆中创建“11”(此时常量池中无对象“11”)。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值