Java String类

String的不可变性

Java.lang包下面的的String在平时很常见,我们都知道String是不可变类。简单说来,不可变类的实例是不能被修改的,每个实例中包含的信息都必须在该实例创建的时候就提供出来,并且在对象的整个生存周期内固定不变。
在String类中,是通过一个不可变的字符数组来存储对象的字符序列的,final关键字使这个变量是不可变的,所以Sting对象的内容也是不可变的。

private final char value[];

很多初学者对于不可变类还是很不能理解,因为通过下面的代码,看起来改变了字符串对象s。

public static void main(String[] args) {
        String s = "abc";
        System.out.println(s);
        s = "cde";
        System.out.println(s);
    }

输出结果为

abc
cde

其实这是因为对String对象的创建和存储机制不了解产生的误解。下面我们来探讨String的对象创建和初始化。

String的对象创建和初始化机制

字符串的声明和初始化主要有以下两种情况:
(1) 对于String s1=new String(“abc”)语句与String s2=new String(“abc”)语句,存在两个引用对象s1、s2,两个内容相同的字符串对象”abc”,它们在内存中的地址是不同的。只要用到new总会生成新的对象。

(2) 对于String s1 = “abc”语句与String s2 = “abc”语句,在JVM中存在着一个字符串池string pool,其中保存着很多String对象,并且可以被共享使用,s1、s2引用的是同一个常量池中的对象。当创建一个字符串常量的时候,例如String s = “abc”,会首先在字符串常量池中查找是否已经有相同的字符串被定义,它的判断依据是String类equals(Object obj)方法的返回值,也就是判断是否有相同的字符串序列。如果已经定义,则直接获取对其的引用,此时不需要创建新的对象,如果没有定义,则首先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。

例如:

String s1=“abc”; //在常量区里面存放了一个”abc”字符串对象
String s2=“abc”; //s2引用常量区中的对象,因此不会创建新的对象
String s3=new String(“abc”) //在堆中创建新的对象
String s4=new String(“abc”) //在堆中又创建一个新的对象

回到String对象的不可变特性

public static void main(String[] args) {
        String s = "abc";
        //在常量区中存放一个"abc"字符串对象,并把s指向这个对象
        System.out.println(s);
        s = "cde";
        //在string pool中查找是否存在"dec",不存在则创建这个对象,并把s指向这个对象
        System.out.println(s);
    }

通过上面的分析,我们可以知道,改变的只是s的指向,而“abc”和“cde”这两个字符串对象并没有改变,内存中字符串对象如下所示:

执行String s = “abc”后s的指向

执行String s = "abc"后s的指向

执行 s = “cde”后s的指向

执行 s = "cde"后s的指向

改变的只是s的指向,而字符串对象的内容并没有发生改变。
另外,在进行字符串的对象创建时我们还可能会使用“+”来进行字符串的连接, 如果用+号来实现String的串接时有两种情况:
1)仅当+号两边均为字符串常量时,才将其+后的结果当做字符串常量,且该结果直接放入String Pool;
2)若+号两边有一方为变量时,+后的结果即当做非字符串常量处理(等同于new String()的效果)。

改变不可变的String对象

如果我们真的需要去改变String对象,那么应该怎么做呢?
这个时候就需要用到java的反射机制了,我们可以通过反射机制去获取String对象的value数组,并修改value字符数组的相应内容,这样子字符串对象也会相应的发生改变。代码如下:

    public static void main(String[] args)  {


        try {
            String s = "abc";
            Field valuefield = String.class.getDeclaredField("value");
            valuefield.setAccessible(true);
            char[] value = (char[]) valuefield.get(s);
            value[1] = '_';
            System.out.println(s);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

输出结果如下:

a_c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值