一、String
String
是只读字符串(不可变),它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符 数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的 String对象。
private final char value[];
每次的+操作:隐式的在堆上new一个和原字符串相同的StringBuilder对象,在调用append方法拼接+后面的字符串。
jdk9 之后底层数据结构发生变化
string类的当前实现将字符存储在char数组中,每个字符使用两个字节(16位)。从许多不同的应用程序收集的数据表明,字符串是堆使用的主要组成部分,而且,大多数字符串对象只包含拉丁-1字符。这 样的字符只需要一个字节的存储空间,因此这样的字符串对象的内部字符数组中的一半空间将被闲置。
目的:节省空间
private final byte[] value;
Stirng类内部维护一个字符数组变化为维护一个字节数组。
同理 StringBuffer
与 StringBuilder
底层也发生变化
二、StringBuffer和StringBuilder
两个都是继承于java.lang.AbstractStringBuilder的,他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和 StringBuilder来进行操作。
char[] value;
另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁(synchronized),所以是 线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的,但是在单线程时,StringBuiler更快。
二、常用方法
1、String
- 返回指定索引处的字符
public char charAt(int index)
按字典顺序比较两个字符串
如果
String
对象按字典顺序排列在参数字符串之前,结果为负整数。 结果是一个正整数,如果String
对象按字典顺序跟随参数字符串。 如果字符串相等,结果为零。底层实现:转化为
char[]
进行比较。
public int compareTo(String anotherString)
- 将指定的字符串连接到该字符串的末尾
public String concat(String str)
- 字符串包含指定的char值序列时才返回true
String
继承自CharSequence
接口,所以参数可以使String
public boolean contains(CharSequence s)
- 是否以开头(结尾)
public boolean endsWith(String suffix)
public boolean startsWith(String prefix)
- 判断字符串相等
public boolean equals(Object anObject)
public boolean equalsIgnoreCase(String anotherString) //比较,忽略大小写
- 将字符串转化为字符、字节数组
public byte[] getBytes()
public char[] toCharArray()
- 返回子字符串第一次在原字符串中出现的索引
public int indexOf(String str)
public int indexOf(String str, int fromIndex) //从指定的索引后的字符串开始判断
public int lastIndexOf(String str) //最后一次在原字符串中出现的位置
public int lastIndexOf(String str, int fromIndex)
- 判空、长度
public int length() //返回字符串长度
public boolean isEmpty() //仅当length()==0返回true
- 替换字符串中的一部分
//将原字符串中的oldchar替换为newchar
public String replace(char oldChar, char newChar)
- 通过字符串分割字符串
//regex:以该字符串为分割线,如果是".",需要转义为"\\."
//limit: 分割后的最大字符串个数,如果可分割的字符串数超过,则超过的部分不进行分割,可为空
public String[] split(String regex, int limit)
- 通过索引截取字符串
public String substring(int beginIndex, int endIndex)
- 转化为大、小写
public String toUpperCase()
public String toLowerCase()
- 删除前后空格
public String trim()
- 其他数据类型转化为字符串形式
该方法为静态
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