stringbuffer判断是否为空_你真的了解String,StringBuffer,StringBuilder的区别吗?

一、概述

对于java的学习者而言,无论是初学者,还是java大师,String对于大家而言,也绝对不会陌生。下面本人就从自己学习的角度大致分析一下String,StringBuffer和StringBuilder这三者的区别和联系。如有不足,欢迎补充说明~

6e684597461eea5f5dd7ba2baf1c6ddb.png

二、String

String类在java的java.lang.String包下面,需要特别说明的是String类是final关键字修饰的,也就是说String类是不能够被继承的,String中的内容一旦被创建后是不能被修改的。

1)我们先来看下面的例子

public class MainTest { public static void main(String[] args) { String str = new String("同一个世界"); str = new String("同一个梦想"); // 原始String对象中str的内容到底变了没有? System.out.println(str); //下面也是一个String的例子 String str2 = "天下太平"; str2 = str2 + "国泰民安"; // 原始String对象中的str2到底变了没有? System.out.println(str2); }}

运行一下,结果如下:

同一个梦想天下太平国泰民安

这个结果好像跟上面说的String中的内容一旦创建就不能被修改相违背啊,怎么回事呢?

如果大家看过String的源码可以发现,String字符串中的内容存储在char数组中的。在判断原始String对象是否改变了,这里需要明白一个问题,在java中相关对象的引用变量一般都存在栈中,而相关的对象都是存在堆中的,栈中的值指向了它所引用的对象(堆中相应的对象的地址)。String对象不可修改指的是对象本身不可修改,而不是引用不可修改。就拿上面的例子str2来讲,String str2 = "天下太平";这行代码的意思是声明一个String类型的引用变量叫做str2,在内存中创建一个String对象(值为"天下太平"),然后把这个对象的引用赋值给变量str2。str2 = str2 + "国泰民安";这行代码执行的效果是在内存中先另外创建了一个String对象(值为"国泰民安"),然后又创建的一个String对象(值为"天下太平国泰民安"),然后把这个新对象的引用赋值给变量str2,而不是把原来的内存中的那个“天下太平”的String对象值变为“天下太平国泰民安”。所以说,上面代码中的原始String对象都没有改变,变的是引用变量的指向位置。这一段话可能有一些绕,理解起来不是那么容易,请多读几遍,反复思考一下。

2)关于equals和==

我们先看下面一点代码

public class MainTest { public static void main(String[] args) { String str = new String("Hello World"); String str2 = "Hello World"; System.out.println("str和str2的equals值相同吗?" + str.equals(str2)); System.out.println("str和str2的==值相同吗?" + (str == str2)); }}

输出结果为:

str和str2的equals值相同吗?truestr和str2的==值相同吗?false

这些结果是怎么输出来的呢?首先我们需要明白equals和==的区别和联系,对于equals而言,它是 Object类中方法如下:

f0552b45264c432e758fb0df684e028d.png

通过在这里查看Object类中equals的方法我们知道,如果一个类没有重写Object类中的equals方法的话,那么它的作用和==的作用是一样的,对于基本数据类型就是比较两者的值时候相等,对象的话,就是比较二者的引用地址是否相同。但是如果用户可以根据自己的需求进行重写equals方法那样的话,equals比较的返回值就和相关的需求相关了,我们来看下String的equals方法:

/** * Compares this string to the specified object. The result is {@code * true} if and only if the argument is not {@code null} and is a {@code * String} object that represents the same sequence of characters as this * object. * * @param anObject * The object to compare this {@code String} against * * @return {@code true} if the given object represents a {@code String} * equivalent to this string, {@code false} otherwise * * @see #compareTo(String) * @see #equalsIgnoreCase(String) */public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false;}

这样我们就可以发现,对于String对象中的equals方法而言,它的目的是比较两个对象所对应的值是否相同,注意仅仅是两个对象的值,跟这两个对象的引用(地址没有任何关系)。但需要注意的是,重写了equals一定要重写hashcode,不然会有问题的。

3)面试常见题,对于下面声明的这个变量创建了几个对象?

String str=new String("xyz"); //创建了几个对象String str2="abc";//创建了几个对象

首先,String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建。

String str = "abc"创建对象的过程

  1. 首先在常量池中查找是否存在内容为"abc"字符串对象
  2. 如果不存在则在常量池中创建"abc",并让str引用该对象
  3. 如果存在则直接让str引用该对象

String str = new String("abc")创建实例的过程

  1. 首先在堆中(不是常量池)创建一个指定的对象"abc",并让str引用指向该对象
  2. 在字符串常量池中查看,是否存在内容为"abc"字符串对象
  3. 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来
  4. 若不存在,则在字符串常量池中创建一个内容为"abc"的字符串对象,并将堆中的对象与之联系起来

所以,上面的问题,“String str=new String("xyz");”创建了一个或者两个对象,原因在于,对于“xyz”这个对象而言,它是存放在字符串的缓冲区中的,不管出现多少遍,都是缓冲区中的那一个。而new String()每次都会创建一个新的对象。所以如果之前创建过“xyz”这个对象的话,那么久创建一个对象,而如果之前要是没有创建过这个字符串的话,那么就会创建两个对象。其次说String str2=“abc”会创建零个或者一个。

String str="a"+"b"+"c"+"d"+"e"+"f"+"g"+"h"+"i"+"j"+"k";

上面这个创建了几个对象?其实也就是一个,因为javac编译的时候可以对字符串常量直接相加的表达式进行优化,不必等到运行期在进行加法处理,而是在编译的时候直接去掉加号,直接将其编译成这些常量。

bdb6172a58e6c16b41cc09558fcb048e.png

二、StringBuffer和StringBuilder

个人理解而言,对于StringBuffer和StringBuilder是对String的一个优化,之前已经说过了,String对象一旦创建后,String中的内容是不能够改变的。每次改变String都会创建新的对象。这样的话会造成相应空间的浪费。介于此jdk额开发人员计出了StringBuffer和StringBuilder,对于后者而言它们的内容是能够动态改变的。而StringBuffer和StringBuilder的区别就在于StringBuffer是线程安全的(StringBuffer中的绝大部分方法都加了同步关键字)而StringBuilder是线程不安全的。验证代码如下:

package com.springboot.kafakademo.demoTest;@Testpublic void testStringBuilderAndStringBuffer(){ //证明StringBuffer线程安全,StringBuilder线程不安全 StringBuffer stringBuffer = new StringBuffer(); StringBuilder stringBuilder = new StringBuilder(); CountDownLatch latch1 = new CountDownLatch(1000); CountDownLatch latch2 = new CountDownLatch(1000); for(int i=0;i<1000;i++){ new Thread(new Runnable() {@Overridepublic void run() { try { stringBuilder.append(1);  } catch (Exception e) { e.printStackTrace(); } finally { latch1.countDown(); } } }).start(); } for(int i=0;i<1000;i++){ new Thread(new Runnable() {@Overridepublic void run() { try { stringBuffer.append(1); } catch (Exception e) { e.printStackTrace(); } finally { latch2.countDown(); } } }).start(); } try { latch1.await(); System.out.println(stringBuilder.length()); latch2.await(); System.out.println(stringBuffer.length()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }

因为StringBuilder是线程不安全的,所以其运行效率会更高,如果一个字符串是在方法里面定义的话,这种情况仅仅可能有一个线程访问它,不存在不安全的因素,这时推荐使用StringBuilder。如果一个实例变量是在类里面定义的,并且在多线程下会访问到,这时最好使用StringBuffer。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值