Javaz中String,StringBuffer,StrigBuilder

一、String

1、String声明的字符串,长度是不可变的,这也是它与StringBuffer和StringBuilder最直观的一个区别。一般初始化方式:String s = "hello world";经过这条语句,JVM的栈内存中产生一个s变量,堆内存中产生hello world字符串对象。s指向了hello world的地址。像上面这种方式产生的字符串属于直接量字符串对象,JVM在处理这类字符串的时候,会进行缓存,产生时放入字符串池,当程序需要再次使用的时候,无需重新创建一个新的字符串,而是直接指向已存在的字符串。

常量池一般就是指字符串常量池,是用来做字符串缓存的一种机制,当我们在程序中写了形如String s = "abc"这样的语句后,JVM会在栈上为我们分配空间,存放变量s和对象”abc“,当我们再次需要abc对象时,如果我们写下:String s1 = "abc"的语句时,JVM会先去常量池中找,如果不存在,则新创建一个对象。如果存在,则直接将s1指向之前的对象”abc“,此时,如果我们用==来判断的话,返回的true。这样做的好处就是节省内存,系统响应的速度加快,(因为省去了对象的创建时间)这也是缓存系统存在的原因。常量池是针对在编译期间就确定下来的常量而言的,如上所说的String类的一些对象。但是,当类被加载后,常量池会被搬到方法区的运行时常量池,此时就不再是静态的了,那么是不是就不能向常量池中添加新的内容了呢(因为我们刚刚说过,常量池是在编译期确定好的)?答案是否定的,我们依然可以在运行时向常量池添加内容!这就是我们说过的String类有个方法叫intern(),它可以在运行时将新的常量放于常量池。

常量池是在编译期确定好的,所以如果我们的语句时String s = "ab"的话,这个是在编译期确定的,会去常量池查找,而此处我们的语句时s2 = s1+"b",s2的值只有在运行期才能确定,所以不会去常量池查找,也就是产生新串。再次提问:那么这里s2的值是在哪儿分配的呢?堆、JVM栈还是运行时常量池?正确回答:s2在堆上分配,因为+的内部实现是用StringBuilder来实现的。String s2 = s1+"b" 内部是这样实现的:String s2 = new StringBuilder(s1).append("b").toString();所以是在堆上来分配的.

关于equals()和hashCode()方法是这样的:如果没有重写Object的hashCode(),那么如果对象调用equals()放回true,则这两个对象调用hashCode()后返回的整数一定相等。此处继续补充:对于Object类而言,原生的equals()方法,必须两个对象的地址和内容都一样才返回true,同时Object类原生的hashCode()是参照对象的地址和内容根据一定的算法生产的。所以原生的hashCode()只有调用equals()返回true才相等。而String类不同,String类重写了Object的equals(),放松了条件,只要对象地址或者内容相等就返回true。同时,String类重写了hashCode()方法,只要内容相等,则调用hashCode返回的整数值也相等,刚刚说了Object类和String类,此处补充下Integer类:Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer a=new Integer(50),则a.hashCode的值就是50 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。注意StringBuffer和Stringbuilder并没有重写

2、String初始化

String对象的另一种初始化方式,就是采用String类提供的构造方法进行初始化。String类提供了16种构造方法,常用的有五种:

String() --------- 初始化一个String对象,表示一个空字符序列

String(String value) --------- 利用一个直接量创建一个新串

String(char[] value) --------- 利用一个字符数组创建

String(char[] value,int offset,int count) --------- 截取字符数组,从offset开始count个字符创建

String(StringBuffer buffer) --------- 利用StringBuffer创建

二、StringBuffer、StringBuilder

  1、初始化

StringBuffer和StringBuilder就是所谓的可变字符串类,共四个构造方法:

StringBuffer()

public StringBuffer(int paramInt)

public StringBuffer(String paramString)

public StringBuffer(CharSequence paramCharSequence)

观察其源码发现,使用StringBuffer()时,默认开辟16个字符的长度的空间,使用public StringBuffer(int paramInt)时开辟指定大小的空间,使用public StringBuffer(String paramString)时,开辟paramString.length+16大小的空间。都是调用父类的构造器super()来开辟内存。这方面StringBuffer和StringBuilder都一样,且都实现AbstractStringBuilder类。

  2、主要方法

二者几乎没什么区别,基本都是在调用父类的各个方法,一个重要的区别就是StringBuffer是线程安全的,内部的大多数方法前面都有关键字synchronized,这样就会有一定的性能消耗,StringBuilder是非线程安全的,所以效率要高些。下面是String,StringBuffer,StringBuilder性能测试

package javaPrimary;-

public class StringSpilt {
public static void main(String[] args) throws Exception {
String string = "0";
int n = 10000;
long begin = System.currentTimeMillis();


for (int i = 1; i < n; i++) {
string += i;
}
// System.out.println("string=" + string);
long end = System.currentTimeMillis();
long between = end - begin;
System.out.println("使用String类:" + between + "ms");


StringBuffer sb1 = new StringBuffer("0");
long begin1 = System.currentTimeMillis();
for (int i = 1; i < n; i++) {
sb1.append(i);
}
// System.out.println("sb=" + sb1);
long end1 = System.currentTimeMillis();
long between1 = end1 - begin1;
System.out.println("使用StringBuffer类所用时间:" + between1 + "ms");


StringBuilder sb2 = new StringBuilder("0");
long begin2 = System.currentTimeMillis();
for (int i = 1; i < n; i++) {
sb2.append(i);
}
// System.out.println("sb=" + sb2);
long end2 = System.currentTimeMillis();
long between2 = end2 - begin2;
System.out.println("使用StringBuilder类所用时间:" + between2 + "ms");
}
}

输出:

使用String类:597ms
使用StringBuffer类所用时间:2ms
使用StringBuilder类所用时间:1ms

虽然这个数字每次执行都不一样,而且每个机子的情况也不一样,但是有几点是确定的,String类消耗的明显比另外两个多得多。还有一点就是,StringBuffer要比StringBuilder消耗的多,尽管相差不明显。


三、String中常见的问题

String s = "123" + "456"内存中产生几个字符串对象?

这是个比较有争议的问题,面试的时候,老师还挺喜欢问,论坛上大家说几个的也有,我给大家分析一下,因为我们前面有提到Java字符串的缓存机制,编译器在编译的时候会进行优化,所以在编译的过程中123和456被合成了一个字符串"123456",因此,如果缓存池中目前没有123456这个对象,那么会产生一个,即"123456",且栈中产生一个引用s指向它,如果缓存池中已经存在"123456",那么将产生0个对象,直接用s指向它。


String中spilt容易被忽略的问题:如果spilt()函数的参数在要分割的字符串中没有怎么办?如String s = "helloworld" ,我现在调用String[] s2 = s.spilt("ee"),返回什么?看下面的程序,返回helloworld


package javaPrimary;

public class StringSpilt {
public static void main(String[] args) {
String s = "helloworld";
String[] s2 = s.split("ee");
for (String string : s2) {
System.out.println(string);
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值