欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深入了解String相关内容

程序员文章站 2022-07-09 18:11:24
...

文章内容是根据文末的文章总结而来,内容不全面也不缜密:

 

一、定义

publ从该类的声明中我们可以看出String是final类型的,表示该类不能被继承,同时该类实现了三个接口:java.io.Serializable、 Comparable<String>、 CharSequence:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence{}

二、属性

private final char value[];

pri  这是一个字符数组,并且是final类型,他用于存储字符串内容,所以从final这个关键字中我们可以看出,String的内容一旦被初始化了是不能被更改的。String其实就是用char[]实现的 例如 String s = “a”; s = “b” ,这并不是对s的修改,而是改变了S的引用,重新指向了新的字符串,以下的图片可以较好的解释:

定义一个字符串:

String s = "abcd";

                                深入了解String相关内容

s中保存了string对象的引用。下面的箭头可以理解为“存储他的引用”。

使用变量来赋值变量

String s2 = s;

                               深入了解String相关内容

s2保存了相同的引用值,因为他们代表同一个对象。

字符串连接

s = s.concat("ef");

                               深入了解String相关内容

s中保存的是一个重新创建出来的string对象的引用。

       所以一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。

riva

private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

static 因为String实现了Serializable接口,所以支持序列化和反序列化支持。Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。

      Java语言提供了对字符串连接运算符的特别支持(+),该符号也可用于将其他类型转换成字符串。字符串的连接实际上是通过StringBuffer或者StringBuilderappend()方法来实现的,字符串的转换通过toString方法实现,该方法由 Object 类定义,并可被 Java 中的所有类继承。

 

      另外为什么String被设计成final?

      1.字符串池是方法区中的一部分特殊存储。当一个字符串被被创建的时候,首先会去这个字符串池中查找,如果找到,直接返回对该字符串的引用。

     2.缓存hashcode: Java中经常会用到字符串的哈希码(hashcode)。例如,在HashMap中,字符串的不可变能保证其hashcode永远保持一致,这样就可以避免一些不必要的麻烦。这也就意味着每次在使用一个字符串的hashcode的时候不用重新计算一次,这样更加高效。

      3.安全性: String被广泛的使用在其他Java类中充当参数。比如网络连接、打开文件等操作。如果字符串可变,那么类似操作可能导致安全问题

       4.不可变对象天生就是线程安全的,因为不可变对象不能被改变,所以他们可以*地在多个线程之间共享。不需要任何同步处理。

        总之,String被设计成不可变的主要目的是为了安全和高效。所以,使String是一个不可变类是一个很好的设计。

        

        另外JDK6和JDK7中对subString()的不同实现:

        JDK 6中的substring中:当调用substring方法的时候,会创建一个新的string对象,但是这个string的值仍然指向堆中的同一个字符数组。这两个对象中只有count和offset 的值是不同的。

                   深入了解String相关内容

下面是证明上说观点的Java源码中的关键代码:

//JDK 6
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

public String substring(int beginIndex, int endIndex) {
    //check boundary
    return  new String(offset + beginIndex, endIndex - beginIndex, value);
}

JDK 6中的substring导致的问题

      如果你有一个很长很长的字符串,但是当你使用substring进行切割的时候你只需要很短的一段。这可能导致性能问题,因为你需要的只是一小段字符序列,但是你却引用了整个字符串(因为这个非常长的字符数组一直在被引用,所以无法被回收,就可能导致内存泄露)。在JDK 6中,一般用以下方式来解决该问题,原理其实就是生成一个新的字符串并引用他

x = x.substring(x, y) + ""

 

JDK 7 中的substring

上面提到的问题,在jdk 7中得到解决。在jdk 7 中,substring方法会在堆内存中创建一个新的数组。

                    深入了解String相关内容

Java源码中关于这部分的主要代码如下:

//JDK 7
public String(char value[], int offset, int count) {
    //check boundary
    this.value = Arrays.copyOfRange(value, offset, offset + count);
}

public String substring(int beginIndex, int endIndex) {
    //check boundary
    int subLen = endIndex - beginIndex;
    return new String(value, beginIndex, subLen);
}

 

参考:

http://www.hollischuang.com/archives/99

http://www.hollischuang.com/archives/1230

http://www.hollischuang.com/archives/1246

http://www.hollischuang.com/archives/1249

http://www.hollischuang.com/archives/1232

http://www.hollischuang.com/archives/1261

http://www.hollischuang.com/archives/1290

http://www.hollischuang.com/archives/61

相关标签: Java String