Java String类

Java的类库中有一个很特殊的类,就是String。我认为它主要特殊在两点。

第一点,它重载了“+”和“+=”操作符。Java不允许程序员重载任何操作符。Java对String的这种设计初衷,是为了程序员能够更方便的使用String这个类,但是却带来了意想不到的结果:重载带来了内存开销。《Java编程思想》书中第十三章也表达了这个意思。

Java重载的实际实现方式,借助了StringBuilder这个类,使用“+”或“+=”时,编译器自动生成了一个StringBuffer实例,依次append字符串,最后用toString方法返回结果。例如

String a = "first";

String b = "second";

String c = a + b;

第三句在实际执行时,等价于:

StringBuilder builder = new StringBuilder();

builder.append(a);

builder.append(b);

String c = builder.toString();

如果StringBuilder对象只有一个,是没有实际增加内存开销的。但我们常常会在一个循环中使用”+“或”+=“对String进行拼接,每次进入循环体,就会生成一个StringBuilder对象,无疑是增大了开销。这时推荐使用StringBuilder来执行字符串的拼接业务。

Java中还有一个类StringBuffer,它的也能处理String的拼接操作。这二者方法几乎一致,区别在于,StringBuffer是线程安全的,而StringBuilder不是。因此,前者更耗资源一些。

 

String的第二个特殊点在于,JVM为String类开辟了常量池。需要关注的是intern()这个native方法

 

/**

     * Returns an interned string equal to this string. The VM maintains an internal set of

     * unique strings. All string literals found in loaded classes'

     * constant pools are automatically interned. Manually-interned strings are only weakly

     * referenced, so calling {@code intern} won't lead to unwanted retention.

     *

     * <p>Interning is typically used because it guarantees that for interned strings

     * {@code a} and {@code b}, {@code a.equals(b)} can be simplified to

     * {@code a == b}. (This is not true of non-interned strings.)

     *

     * <p>Many applications find it simpler and more convenient to use an explicit

     * {@link java.util.HashMap} to implement their own pools.

     */

    public native String intern();

所有已经被加载过的类的字面值(即双引号引起来的字符串),都被存储到常量池里去了。常量池里的String是unique的,也就是存储时会先检查,如果已经有了这个字符串,将不会添加而是直接返回已有常量的引用。也就是说

String str = new String(“abc”);

new 代表在堆里分配了空间,里边放了"abc"这个String对象。需要说明的是"abc"这个字面值去哪儿了?JVM会检查它"maintains an internal set of unique strings",也就是看看这个常量池中是否已有"abc",如果没有会把"abc"添加到常量池中去!这种情况下,这行代码实际上创建了两个对象,一个在堆里,一个在常量池里。

intern方法是public的,它的作用是将String对象的字串存入常量池。

 

有意思的是String对象是不可被修改的,每一个构造的String对象,都是作为常量存储在内存中。引用可以指向别的位置,但对象本身没有被改变,对象本身一直作为常量存在,直到被回收。

还拿上边的三句来说明

String a = "first";

实际上是先为"first"这个常量开辟一块空间,然后声明实例化String的对象a。实例化a的过程是,JVM先扫描是否存在"first"这个String常量,如果有,将a指向它。此时"first" == a 值为true。如果我们定义

String a1 = "first";

那么 a1 == a也是为true的。对a或a1的任何操作所带来的字符串的改变都将产生新的String对象,并将a或a1重新指向这个新生成的对象。比如

a += "A";

此时,执行的步骤是:

1、生成”A"String对象;

2、拼接"first"和"A",步骤见上;

3、返回的"firstA"称为新的String对象,将a指向"firstA"。这时 a != a1。

 

关于这一段的理解,可以对比:

Integer i = new Integer(2);

Integer j = new Integer(2);

 

System.err.println(i == 2);//true

System.err.println(j == 2);//true

System.err.println(j == i);//false

 

使用String时的注意事项:

1、subString生成的对象会保留对原String对象的引用,如果原String对象比较大且又不再被需要,使用new String(src.subString(params...))。

2、""和null是不一样的,就像空数组和NULL不一样,是一个道理。

3、要学会使用正则表达式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值