1.new 和直接创建的区别
String aa = "aa";
String bb = "aa";
//true
System.out.println(aa == bb);
//true
System.out.println(aa.equals(bb));
String ab = new String("ab");
String bc = new String("ab");
//false
System.out.println(ab == bc);
//true
System.out.println(ab.equals(bc));
直接创建就是在常量池中创建一个"aa" 然后 aa 对象和 bb这两个对象分别去引用常量池里的这个"aa" 所以它们相等。
用new 创建就是在堆内存里创建一个新的对象,两个对象指向不同的引用,所以它们不相等。
再看equals方法,在String源码中重写了equals方法
public boolean equals(Object anObject) {
//首先判断这两个对象是否是同一个对象,== 即比较地址
if (this == anObject) {
return true;
}
//接着比较类型,传进来的anObject是否是String类型的
if (anObject instanceof String) {
String anotherString = (String)anObject;
//n代表当前String对象的char[]长度
int n = value.length;
//比较两个对象的长度
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//比较char[]里每个位置的值是否相等
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
因为上面两个equals方法比较的都是字符串是否相等,所以返回的都是true
2. final不可变
在String源码中,用来存储字符的char[] 被final修饰,所以String对象是不可变的,并且class也被final修饰,说明String是不可继承的
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
这样做的好处是什么?
- 保证String对象的安全,防止被恶意修改
- 保证hash值不会被频繁修改,确保唯一性
- 可以实现字符串常量池,这种方法可以减少同一个值的字符串的创建,减少内存的使用
3.indexOf()方法
方法
-
public int indexOf(int ch): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
public int indexOf(int ch, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
int indexOf(String str, int fromIndex): 返回从 fromIndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
参数
-
ch – 字符,Unicode 编码。
-
fromIndex – 开始搜索的索引位置,第一个字符是 0 ,第二个是 1 ,以此类推。
-
str – 要搜索的子字符串
String s = "aaa456ac";
//7
System.out.println(s.indexOf("c"));
//3
System.out.println(s.indexOf('4'));
//6
System.out.println(s.indexOf("a",4));
//7 因为unicode编码99代表的就是c
System.out.println(s.indexOf(99));
4.intern()方法
首先看一下常量池的概念:
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。 常量池的实现是类似与一个HashTable的实现方式。
intern方法就是Java在常量池中寻找是否有这个字符串,如果有,则返回它的引用,如果没有则在常量池中创建,并返回它的引用。
public native String intern();
String a = new String("abc").intern();
String b = new String("abc").intern();
if (a == b) {
//a==b
System.out.print("a==b");
}
返回结果是a==b,说明他们是同一个引用。
String a = new String(“abc”).intern(),会先去堆中创建一个对象,然后在常量池中寻找是否有这个字符串,因为没有,所以创建一个,再返回,a指向的就是常量池那个字符串的引用,那么之前创建的那个对象就会被GC回收。
b创建的时候也是一样的,因为之前a已经在常量池中创建过了,所以直接返回引用,a和b 指向的就是同一个引用,所以他们是相等的。
5. spilt()方法
split() 方法根据匹配给定的正则表达式来拆分字符串。
public String[] split(String regex, int limit)
参数
-
regex – 正则表达式分隔符。
-
limit – 分割的份数。
String a = "a-a-b";
String [] b= a.split("-");
/**
*a
*a
*b
**/
for (String s : b) {
System.out.println(s);
}
String c = "a-a-c";
String [] d= c.split("-",2);
/**
*a
*a-c
**.
for (String s : d) {
System.out.println(s);
}
6.replaceAll()方法
replaceAll() 方法使用给定的参数 replacement 替换字符串所有匹配给定的正则表达式的子字符串。
public String replaceAll(String regex, String replacement)
参数
-
regex – 匹配此字符串的正则表达式。
-
replacement-- 用来替换每个匹配项的字符串。
String id = UUID.randomUUID().toString();
System.out.println(id);
System.out.println(id.replaceAll("-",""));
Java中常见的生成随机字符串的方法UUID,因为经常需要去除中间的"-"这个字符,所以会使用到replaceAll()这个方法。
上面的方法返回值如下
8a9d366b-57b0-4ee2-bebb-39a74a58483a
8a9d366b57b04ee2bebb39a74a58483a
使用空串,替换了"-"。
7.subString()
substring() 方法返回字符串的子字符串。
-
public String substring(int beginIndex)
-
public String substring(int beginIndex, int endIndex)
参数
-
beginIndex – 起始索引, 索引从 0 开始。
-
endIndex – 结束索引。
String s = "aabbcc";
//bbcc
System.out.println(s.substring(2));
//bbc
System.out.println(s.substring(2,5));
8.String和StringBuilder,StringBuffer的区别
从几个方面来区分,可变性,线程安全性,对象引用
-
可变性
String的char[]数组是final修饰的,所以是不可变的。但是StringBuilder和StringBuffer并没有被final修饰,所以是可变的 -
线程安全性
String的char[]数组是final修饰的,所以也就可以理解为一个常量,所以是线程安全的。而StringBuilder并没有做同步,是线程不安全的。StringBuffer做了同步,是线程安全的。 -
对象引用
String进行改变的时候,都会生成一个新的字符串,并将指针指向它。而StringBuilder和StringBuffer都是在本身的字符串上修改。