一段简单的关于字符串的 Java 代码竟考察了这么多东西

  下面的代码运行结果是什么?解释一下为什么会有这些差异。
  
  复制代码
  
  String s1 = "hello";
  
  String s2 = s1 + ",world";
  
  String s3 = "hello" + ",world";
  
  String s4 = "hello,world";
  
  String s5 = new String("hello,world");
  
  System.out.println(s2.equals(s4)); // true
  
  System.out.println(s2==s4);     // false
  
  System.out.println(s3==s4);     // true
  
  System.out.println(s4==s5);     // false
  
  复制代码
  
  看似简单的代码,却有很多学问在里面。在此之前先非常简单地说一下JVM中的栈和堆,栈一般存放的是局部变量,对象的引用等;堆一般放对象、常量等,在 jdk1.7 以后的版本,字符串常量池也在堆中。
  
  字符串常量池
  
  一般情况下,创建字符串对象有两种方式,一种是字面值创建,一种是 new 创建,这两者是不一样的。
  
  如果是字面值创建的方式,如 String s4="hello,world",JVM会先去字符串常量池中寻找有没有“hello,world”这个字符串,若有,则将其地址给 s4;若没有,则先在常量池里创建“hello,world”,然后再把地址给s4;而通过 new 的方式创建对象,则是在堆中创建“hello,world”对象,s5 指向这个对象。还是看图吧,比较直观:
  
  equals() 和 ==
  
  这是 Java 面试常考点,有人可能会说 equals() 比较的是值,而 == 比较的是对象的引用(没错是我),直到被面试官吊打,才会老老实实去看 Object 类中 equals() 的源码:
  
  可以看到,equals() 方法体中,返回的是 两个对象的引用的比较结果 。但是,两个 String 类型 == 和 equals() 有时候结果却不一样。那是因为在 String 类中,equals() 被重写了:
  
  可以看到,它先对比两个引用;若一样则直接返回 true,不一样就对它们的值进行比较。因此,可以对 == 和 equals() 更好的区别了:
  
  字符串的拼接
  
  String 类被 final 修饰,因此字符串不能修改,当两个字符串相加时,是先生成 StringBuilder 对象,然后通过 append() 的方式将两个字符串拼接,再调用 toString() 方法生成新的字符串对象。所以只考虑 s1 和 s2,他们在 JVM 中是这样的:
  
  但是像 String s3 = "hello" + ",world" 这样直接两个字面值相加的,java文件在编译期间就已经将这条语句做了优化,将其直接变成 "hello,world",等到运行的时候就查找字符串常量池,因此 s3 == s4 返回的结果就为 true。
  
  String、StringBuilder、StringBuffer
  
  既然上面说到了 StringBuilder,那就顺便简单说一下这三者之间的区别。
  
  String 是常量,且每次需要在原来字符串基础上扩展都需要新建对象,导致速度会稍微慢一点,而且占内存,因此对于需要经常扩展的字符串,可以使用 StringBuilder 和 StringBuffer。但是 StringBuilder 和 StringBuffer 又有区别,即时两者很像,在速度上 StringBuilder 会快一些,因为 StringBuffer 的操作加了 synchronized ,即加了锁,使得操作相对比较慢,就举 append() 为例子,StringBuffer 中此方法的源码如下:
  
  所以,一般在单线程的情况下,可以选择使用 StringBuilder;在多线程的情况下可以选择 StringBuffer .
  
   public class AtomicProblem {
  
  private static Logger logger = LoggerFactory.getLogger(AtomicProblem.class);
  
  public static final int THREAD_COUNT = 10;
  
  public static void main(String[ www.yongshiyule178.com] args) throws Exception {
  
  BankAccount  sharedAccount = new BankAccount("account-csx",0.00);
  
  ArrayList<Thread> threads = new ArrayList<www.zhenghongyule.com>();
  
  for (int i = 0; i www.javachenglei.com< THREAD_COUNT; i++) {
  
  Thread thread = new Thread(new Runnable(www.dajuhezc.cn ) {
  
  @Override
  
  public void run(www.huizhonggjzc.cn ) {
  
  for (int j = 0; j < 1000 ;www.baihuajtuan.cn j++) {
  
  sharedAccount.deposit(10.00);

  public double deposit(double amount){
  
  balance = balance + amount;
  
  return balance;

  public double withdraw(double amount){
  
  balance = balance - amount;
  
  return balance;
  
  public String getAccountName() {
  
  return accountName;
  
  public void setAccountName(String accountName) {
  
  this.accountName = accountName;
  
  回到一开始的程序,我们可以大致地画出在 JVM 中的存储:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值