字符串


一、String

String

是只读字符串(不可变),它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符 数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的 String对象。

private final char value[];

每次的+操作:隐式的在堆上new一个和原字符串相同的StringBuilder对象,在调用append方法拼接+后面的字符串。

jdk9 之后底层数据结构发生变化

string类的当前实现将字符存储在char数组中,每个字符使用两个字节(16位)。从许多不同的应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且,大多数字符串对象只包含拉丁-1字符。这 样的字符只需要一个字节的存储空间,因此这样的字符串对象的内部字符数组中的一半空间将被闲置。

目的:节省空间

private final byte[] value;

Stirng类内部维护一个字符数组变化为维护一个字节数组。

同理 StringBufferStringBuilder 底层也发生变化

二、StringBuffer和StringBuilder

两个都是继承于java.lang.AbstractStringBuilder的,他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和 StringBuilder来进行操作。

char[] value;

另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁(synchronized),所以是 线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的,但是在单线程时,StringBuiler更快。

二、常用方法

1、String

  1. 返回指定索引处的字符
public char charAt(int index)
  1. 按字典顺序比较两个字符串

    如果String对象按字典顺序排列在参数字符串之前,结果为负整数。 结果是一个正整数,如果String对象按字典顺序跟随参数字符串。 如果字符串相等,结果为零。

    底层实现:转化为char[]进行比较。

public int compareTo(String anotherString)
  1. 将指定的字符串连接到该字符串的末尾
public String concat(String str)
  1. 字符串包含指定的char值序列时才返回true

String继承自CharSequence接口,所以参数可以使String

public boolean contains(CharSequence s)
  1. 是否以开头(结尾)
public boolean endsWith(String suffix)    
public boolean startsWith(String prefix)
  1. 判断字符串相等
public boolean equals(Object anObject)
public boolean equalsIgnoreCase(String anotherString)  //比较,忽略大小写
  1. 将字符串转化为字符、字节数组
public byte[] getBytes()
public char[] toCharArray()
  1. 返回子字符串第一次在原字符串中出现的索引
public int indexOf(String str)
public int indexOf(String str, int fromIndex)  //从指定的索引后的字符串开始判断

public int lastIndexOf(String str)    //最后一次在原字符串中出现的位置
public int lastIndexOf(String str, int fromIndex)
  1. 判空、长度
public int length()        //返回字符串长度
public boolean isEmpty()    //仅当length()==0返回true
  1. 替换字符串中的一部分
//将原字符串中的oldchar替换为newchar
public String replace(char oldChar, char newChar)
  1. 通过字符串分割字符串
//regex:以该字符串为分割线,如果是".",需要转义为"\\."
//limit: 分割后的最大字符串个数,如果可分割的字符串数超过,则超过的部分不进行分割,可为空
public String[] split(String regex, int limit)
  1. 通过索引截取字符串
public String substring(int beginIndex, int endIndex)
  1. 转化为大、小写
public String toUpperCase()
public String toLowerCase()
  1. 删除前后空格
public String trim()
  1. 其他数据类型转化为字符串形式

该方法为静态

public static String valueOf(Object obj)

三、难点

1、String的intern()方法

本地方法,如果字符串常量池中已经包含一个等于此String对象的字符串,则返回池中这个字符串对象的引用;否则,会将此String对象包含的字符串添加到字符串常量池中,并返回此字符串的引用。

String s1 = new StringBuilder("ja").append("va").toString();
System.out.println(s1);     //java
System.out.println(s1.intern());    //java
System.out.println(s1==s1.intern());    //false

System.out.println("================================");

String s2 = new StringBuilder("hello").append("world").toString();
System.out.println(s2);     //helloworld
System.out.println(s2.intern());    //helloworld
System.out.println(s2==s2.intern());    //true

为什么s1==s1.intern()s2==s2.intern()不一样?

实际上,只有java字符串会出现true。那么另外一个java字符串是如何加载进来的?

有一个初始化的java字符串(JDK娘胎自带的),在加载sun.misc.version这个类的时候进入常量池。

//字符串常量池中本身自带一个java
//new一个字符串对象“java”
String s1 = new StringBuilder("ja").append("va").toString();
//s1指向的是自己new的java字符串
System.out.println(s1);     //java
//因为字符串常量池中原本就有“java”字符串,所以s1.intern()指向的原有的java,而不是我们new出来的
System.out.println(s1.intern());    //java
System.out.println(s1==s1.intern());    //false

  目录