- 本文主要记录
- String的简单概述、常用构造方法、常用API、方法区和堆内存的补充
- StringBuffer和StringBuilder的简单介绍、常用API和简单的区别
一、String
1、概述
- String表示字符串,java程序中所有字符串文字都是String类的实例(对象)
- 字符串是不可变的,它的值在创建后无法更改
- 因为String的底层是使用char类型的数组存储字符串的
- 数组的长度一旦确定,无法再更改
- 因为String的底层是使用char类型的数组存储字符串的
- 如果多个字符串的内容完全一致,它们在内存中就会共享同一块内存
- 相同的字符串缓存在同一块内存区域,是通过字符串常量池实现的
- 这里指的是直接使用String xx = "xxx"的格式定义的字符串,才会放在字符串常量池
- 如果是使用String的构造器创建出的字符串,并不是放在常量池,而是在堆内存新开辟一块空间进行存储,此对象与字符串常量池中相同内容的对象不是同一个对象
- String是final修饰的,无法被继承
2、常用构造方法
String()
- 创建一个String对象,表示的是空字符序列
- 是在堆内存中新开辟一块空间
String(String str)
- 根据传入的字符串创建一个String对象
String(byte[] bytes, String charsetName)
- charsetName表示的是字符集编码
- 该构造器是根据字符集编码,将字节数组转换成String对象
- 应用场景
- 当浏览器在发数据的时候,如果tomcat版本比较老(比如8版本之前),有些参数会乱码。这时候可以将这些字符串变成字节数组,再通过此构造器指定编码,将内容重新组合,以实现消除乱码的效果
3、常用API
char charAt(int index)
- 返回指定索引处的char值
- 底层源码
- 如果index不在数组索引范围内,会跑出异常
- 否则返回char数组索引对应的值
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
boolean contains(CharSequence s)
- 当且仅当此字符串包含指定的char序列时返回true
- CharSequence是String的父接口
- 底层源码分析
- 调用String的indexOf方法,判断传入的字符值序列在字符数组中的索引是否大于-1
- 即判断是否存在,存在则大于-1,返回true;不存在则等于-1返回false
- 调用String的indexOf方法,判断传入的字符值序列在字符数组中的索引是否大于-1
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
boolean equals(Object anObject)
- 将此字符串与指定的对象进行比较
- 区分大小写
- 底层源码分析
- 先使用==判断,如果传入的对象和当前字符串是同一个对象,返回true
- 假设字符串的内容全部一致,分为下面几种情况
- "abc" == "abc" --true : 因为都在字符串常量池
- "abc" == new String("abc") --false:前者在字符串常量池,后者是在堆内存新开辟一块空间
- new String("abc") == new String("abc") --false: 两者在堆内存的不同空间里
- 假设字符串的内容全部一致,分为下面几种情况
- 如果使用==判断为false,再判断传入的对象是否是String类(用了instanceof关键字)
- 如果是,则将传入对象转化为字符串,内容逐个和当前字符串比较
- 完全一致返回true,否则返回false
- 如果传入的对象不是String,返回false
- 如果是,则将传入对象转化为字符串,内容逐个和当前字符串比较
- 先使用==判断,如果传入的对象和当前字符串是同一个对象,返回true
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
int indexOf(int ch)
- 返回指定字符第一次出现的字符串中的索引
- 还有几个重载方法,简单介绍一下
- 传入的参数可以是int类型,也可以是String类型
- 此外还可以多加一个起始索引参数,从该起始索引位置开始搜索
- 底层代码判断索引是否越界,然后使用一定的算法遍历进行比较
int lastIndexOf(int ch)
- 返回指定字符最后一次出现的字符串中的索引
String[] split(String regex)
- 根据regex划分字符串,划分结果存在字符串数组中
- 举例
String s1 = "a,b,c,d,e";
String[] arrs = s1.split(",");
for (String arr : arrs) {
System.out.println(arr);
}
String substring(int beginIndex)和String substring(int beginIndex, int endIndex)
- 返回一个字符串,该字符串是此字符串的子字符串
- 一个参数
- 从该索引位置直到数组末尾的内容作为子字符串
- 两个参数
- 包含起始索引,不包含结束索引的内容作为子字符串
- 一个参数
- 举例
String s1 = "123456789";
System.out.println(s1.substring(2)); //3456789
System.out.println(s1.substring(2,4)); //34
String toUpperCase()和String toLowerCase()
- toUpperCase()将此字符串的所有字符转化为大写
- toLowerCase()将此字符串的所有字符转化为小写
- 举例
String s = "Abc123AABBccdd";
System.out.println(s.toUpperCase()); //ABC123AABBCCDD
System.out.println(s.toLowerCase()); //abc123aabbccdd
String trim()
- 删除字符串前面和后面所有的空格
- 中间的空格不会删除
- 举例
String s = " abc d ";
System.out.println(s.trim()); //abc d
static String valueOf(不同基本数据类型的数据)
- 将传入的基本数据类型的数据变成字符串
- 举例
System.out.println(String.valueOf(123)); //123
System.out.println(String.valueOf(0)); //0
char[] a = {'a','b','c','d'};
System.out.println(String.valueOf(a,0,2)); //ab
boolean endsWith(String suffix)
- 判断字符串是否以指定的后缀结尾
4、方法区和堆内存补充
a、方法区
- 就是加载代码的内存区域,存储的是可以重复使用的资源,包括
- 类信息(构造方法/接口定义)
- 静态资源
- 常量
- 成员方法
- 运行时常量池
- 方法区是被所有线程共享的
- 所有定义的方法都保存在该区域,此区域属于共享空间
- jdk1.7之前
- hotspot虚拟机对方法区的实现为永久代
- hotspot是JVM虚拟机的概念
- 永久代
- 永久在内存中村相互的内容
- 加载一次之后,除非程序结束,否则可以一直复用
- 不会被GC回收
- 运行时常量池(包括字符串常量池)存放在方法区
- hotspot虚拟机对方法区的实现为永久代
- jdk1.7
- 字符串常量池从方法区移到了堆内存
- 运行时常量池
- 除了字符串常量池,剩下的都还在方法区,也就是hotspot的永久代
- 这里的永久代区别于后面堆中的永久代,方法区不是在堆中的,这里只是表明方法区在这个时候是用永久代的方式实现的
- jdk1.8及之后
- hotspot移除了永久代,用元空间代替
- 元空间和永久代其实是一个概念,只是处理机制有一点不同
- 元空间使用的是本地内存,而不是JVM内存
- 元空间和永久代其实是一个概念,只是处理机制有一点不同
- 字符串常量池还在堆内存中
- 运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间
- hotspot移除了永久代,用元空间代替
b、堆内存
- 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的
- 堆内存存储对象和数组数据
- 堆的分类
- 新生代
- 刚创建的对象都存储在新生代里
- 新生代的GC机制(java的垃圾回收处理机制)不一样,GC机制运转的很快
- 因为刚创建的对象很可能马上就不会再使用了(没有被引用),所以GC需要经常进行内存回收处理
- 如果新生代的对象连续15次回收都没被回收成功,说明该对象使用的时间可能会比较久,就把该对象从新生代转移到老年代
- 老年代
- 老年代里的对象,GC很久才访问一次,节省内存
- 永久代
- 永久代里面的内容不会被GC访问,即不会被回收
- 新生代
二、StringBuilder和StringBuffer
a、概述
- 在进行大量的字符串拼接时,需要避免使用String直接拼接
- 使用String拼接的本质是在堆内存中不断创建新的char数组进行数据拼接,而不用的字符串因为存在于常量池,不会被GC回收,会造成大量的内存空间浪费,甚至导致内存溢出
- 由此引入了StringBuffer和StringBuilder,用于字符串的拼接操作
- 两者都是可变字符序列
- 字符内容存储在数组中,调用这两个类的构造器产生数组的初始容量都是16个字符,存满了会动态扩容
- 它们在拼接字符串的时候,在内存中没有进行缓存,每一次产生的垃圾都会被及时回收
b、特点
- StringBuilder的拼接效率比StringBuffer高
- StringBuffer是线程安全,StringBuilder是线程不安全的
c、常用API
- append()
- 用于拼接字符串
- toString()
- 用于将StringBuilder或StringBuffer对象转换成字符串
- reserve()
- 用于将字符串反转
# 本文写得没有特别详细,其他的知识点日后完善...