常量池:专门存储常量的地方,包括了类,方法,接口中的常量以及字符串常量。
编译常量池: 把字节码加载斤JVM的时候,存储的是字节码的相关信息.(不研究)
运行常量池: 存储常量数据(研究).
字符序列:把多个字符按照一定得顺序排列起来。
字符串:把多个字符串串联起来.
字符串的分类:
1)不可变的字符串:
String:当前对象创建完毕之后,该对象的内容(字符序列)是不能改变的,一旦内容改变就是一个新的对象.
2)可变的字符串:
StringBuilder/StringBuffer:当对象创建完毕之后,该对象的内容可以发生改变,当内容发生改变的时候,对象保持不变.
提示:String、StringBuffer和StringBuilder都实现了CharSequence接口,因此CharSequence可认为是一个字符串的协议接口。
一、java.lang.StringString类:表示不可变的字符串,当前对象创建完毕之后,该对象的内容(字符序列)是不能改变的,一旦内容改变就是一个新的对象.
字符串的本质(底层是什么其实就是char[]),char表示一个字符。比如:
String str = “ABCDEFG”; //定义一个字符串对象,等价于
char[] cs = new char[]{'A','B','C','D','E','F','G'};
深入理解参考文章:深入理解Java中的String(大坑)
1、String对象的创建:
1)直接赋一个字面量: String str1 = “ABCD”;
2)通过构造器创建: String str2 = new String(“ABCD”);
面试题:两种方式有什么区别,分别在内存中如何分布?
/**
* 最多创建一个String对象,最少不创建String对象.
* 如果常量池中,以及存在"ABCD",那么str1直接引用,此时不创建String对象;否则,先在常量池先创建"ABCD"内存空间,再引用.
*/
String str1 = "ABCD";
/**
* 最多创建两个String对象,至少创建一个String对象.
* new关键字:绝对会在堆空间中创建内存区域. 所以至少创建一个String个对象.
*/
String str2 = new String("ABCD");
2、String对象的空值 和 判断字符串对象非空或为空
public class StringUtils {
/**
* 判断字符串为空
* @param str
* @return true - 为空,false - 非空
*/
public static boolean isBlank(String str) {
return !isNonBlank(str);
}
/**
* 判断字符串为非空
* @param str
* @return true - 非空,false - 为空
*/
public static boolean isNonBlank(String str) {
if (str != null && !"".equals(str.trim())) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
/**
* String对象的空值:
* 引用不能为空(null) 或者 字符内容不能为空字符串("")
*/
// 没有初始化,没有分配内存空间
String str1 = null;
// 已经初始化,分配内存空间,不过没有内容
String str2 = "";
String str3 = " ";
System.out.println(StringUtils.isNonBlank(str1)); // false
System.out.println(StringUtils.isNonBlank(str2)); // false
System.out.println(StringUtils.isNonBlank(str3)); // false
System.out.println(StringUtils.isBlank(str3)); // true
}
}
3、字符串的比较操作:
1)使用 == 号: 只能比较引用的内存地址是否相同.
2)使用equals方法: 在Object类中和”==”号相同,建议子类覆盖equals方法去比较自己的内容。String类覆盖了equals方法,所以比较的是字符内容.
面试题:String对象比较
public static void main(String[] args) {
String str1 = "ABCD";
String str2 = "A" + "B" + "C" + "D";
String str3 = "AB" + "CD";
String str4 = new String("ABCD");
String temp = "AB";
String str5 = temp + "CD";
String str6 = getAB() + "CD";
/**
* 面试题:String对象比较:
* 1、单独使用 "" 引号创建的字符串都是直接量,编译期就已经确定存储到常量池中;
* 2、使用 new String("") 创建的对象会存储到堆内存中,是运行期才创建;
* 3、使用只包含直接量的字符串连接符如 "aa" + "bb" 创建的也是直接量,编译期就能确定,已经确定存储到常量池中(str2和str3);
* 4、使用包含 String直接量(无final修饰符)的字符串表达式(如"aa" + s1)创建的对象是运行期才创建的,存储在堆中;
* 5、通过变量/调用方法去连接字符串,都只能在运行时期才能确定变量的值和方法的返回值,不存在编译优化操作.
*/
System.out.println(str1 == str2); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str1 == str4); // false
System.out.println(str1 == str5); // false
System.out.println(str1 == str6); // false
}
private static String getAB() {
return "AB";
}
4、String类中的常用方法
1)获取字符串信息
-
-
char
charAt(int index)
返回
char
指定索引处的值。int
indexOf(String str)
返回指定子字符串第一次出现的字符串内的索引。
int
indexOf(String str, int fromIndex)
返回指定子串的第一次出现的字符串中的索引,从指定的索引开始。
boolean
isEmpty()
返回
true
如果,且仅当length()
为0
。int
lastIndexOf(String str)
返回指定子字符串最后一次出现的字符串中的索引。
int
lastIndexOf(String str, int fromIndex)
返回指定子字符串的最后一次出现的字符串中的索引,从指定索引开始向后搜索。
int
length()
返回此字符串的长度。
boolean
startsWith(String prefix)
测试此字符串是否以指定的前缀开头。
boolean
startsWith(String prefix, int toffset)
测试在指定索引处开始的此字符串的子字符串是否以指定的前缀开头。
boolean
endsWith(String suffix)
测试此字符串是否以指定的后缀结尾。
String
trim()
返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
-
public static void main(String[] args) {
String str1 = "ABCD";
System.out.println(str1.charAt(1)); // B
System.out.println(str1.indexOf("B")); // 1
System.out.println(str1.isEmpty()); // false. 注意 str1 != null
System.out.println(str1.lastIndexOf("D")); // 3
System.out.println(str1.length()); // 4
System.out.println(str1.startsWith("A")); // true
System.out.println(str1.endsWith("D")); // true
}
2)字符串比较判断
-
-
boolean
equals(Object anObject)
将此字符串与指定对象进行比较。
boolean
equalsIgnoreCase(String anotherString)
将此
String
与其他String
比较,忽略大小写。
-
String str1 = "ABCD";
String str2 = "abcd";
System.out.println(str1.equals(str2)); // false
System.out.println(str1.equalsIgnoreCase(str2)); // true
3)字符串大小写转换
-
-
String
toLowerCase()
将所有在此字符
String
使用默认语言环境的规则,以小写。String
toUpperCase()
将所有在此字符
String
使用默认语言环境的规则大写。
-
String str1 = "AbcD";
System.out.println(str1.toUpperCase()); // ABCD
System.out.println(str1.toLowerCase()); // abcd
二、StringBuffer 与 StringBuilder
StringBuffer JDK1.0版本提供的类,线程安全,做线程同步检查, 效率较低。
StringBuilder JDK1.5版本提供的类,线程不安全,不做线程同步检查,因此效率较高,官方推荐使用
在使用上,二者的方法大部分都是一样的,使用下 StringBuilder类,覆盖了toString()方法
-
-
StringBuilder
append(String str)
将指定的字符串附加到此字符序列。
int
capacity()
返回当前容量。
StringBuilder
delete(int start, int end)
删除此序列的子字符串中的字符。
StringBuilder
deleteCharAt(int index)
删除
char
在这个序列中的指定位置。int
indexOf(String str)
返回指定子字符串第一次出现的字符串内的索引。
StringBuilder
insert(int offset, String str)
将字符串插入到此字符序列中。
int
lastIndexOf(String str)
返回指定子字符串最右边出现的字符串内的索引。
int
length()
返回长度(字符数)。
StringBuilder
replace(int start, int end, String str)
用指定的String中的字符替换此序列的子字符串中的
String
。StringBuilder
reverse()
导致该字符序列被序列的相反代替。
String
substring(int start)
返回一个新的
String
,其中包含此字符序列中当前包含的字符的子序列。String
substring(int start, int end)
返回一个新的
String
,其中包含此序列中当前包含的字符的子序列。String
toString()
返回表示此顺序中的数据的字符串。
-
StringBuilder sb = new StringBuilder();
sb.append("aAAbBB").append("CccdDD");
System.out.println(sb); // aAAbBBCccdDD
System.out.println(sb.capacity()); // 16
System.out.println(sb.deleteCharAt(2)); // aAbBBCccdDD
System.out.println(sb.indexOf("a")); // 0
System.out.println(sb.length()); // 11
System.out.println(sb.toString()); // aAbBBCccdDD
System.out.println(sb.replace(0, 2, "==")); // ==bBBCccdDD
System.out.println(sb.substring(2, sb.length())); // bBBCccdDD
System.out.println(sb.reverse()); // DDdccCBBb==
StringBuIlder 类的扩容机制:默认长度均为 16
JDK 1.8中,查看 AbstractStringBuilder 父类源码
可以看出,扩容的大小是新字符串的长度的2倍,然后再加上2。
为什么要再加上2呢?
来自网上一个合理的回答:在使用StringBuilder的时候,append()之后,我们一般会在后面在加上一个分隔符,例如逗号,也就是再加上一个char,而char在java中占2个字节,避免了因为添加分隔符而再次引起扩容。不得不佩服JDK开发者的高瞻远瞩!
ends ~