String(字符串).
final
类:不可被继承,不可变的字符序列- 支持序列化
- 可以比较大小
- 字符串是常量,用双引号表示,其值不可更改(String a = “1”; ==> String a = “12”; 这个不叫更改字符串,只是更改字符串引用指向另一个常量,也就是重新赋值)
- 不会在原内存区域进行修改
- 看似的修改操作(赋值,连接,replace()等),都是会重新指定内存区域的
JDK1.8及以前
字符内容是存储在一个字符数组private final char value[]
中JDK1.9及以后
字符内容是存储在一个字节数组private final byte value[]
中
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc
1、定义方式.
- 字面量定义
- new
//字面量
String str1 = "abc";
String str2 = "abc";
str1 == str2 //true : ? ==> 字面量是存储常量池中的,常量池中不会存储相同内容的字符串,所以这两个变量指向同一个常量(地址)
//new
//带参:字符串
String str3 = new String("s");
String str4 = new String("s");
str3 == str4 //false : ? ==> 创建了两个对象,这两个对象是不同的内存空间
//无参
String str5 = new String();// 长度为0的字符串
//带参:char[]
char[] ch;
String str6 = new String(ch);
//String str6 = new String(char[] ch, int startIndex, int count);
//带参: byte[]
byte[] b;
String str7 = new String(b);
//带参:StringBuffer or StringBuilder
String buf = new String(new StringBuffer("11"));
String buf = new String(new StringBuilder("22"));
...
...
...
2、基本方法使用.
常用方法.
1.int length()
.
返回字符串的长度
2.char charAt(int index)
.
返回某索引处的字符
3.boolean isEmpty()
.
判断字符串是否为空字符串
- “”
- new String();
- new String(“”)
boolean isBlank()
JDK11
可以判断字符串是否是空串或是空白的
4.String toLowerCase()
.
将String中所有字符转为小写
5.String toUpperCase()
.
将String中所有字符转为大写
6.String trim()
.
返回字符串的副本,忽略前导空白和尾部空白
7.boolean equals(Object obj)
.
比较字符串的内容是否相同
8.boolean equalsIgnoreCase(String anotherString)
.
忽略大小写比较内容是否相等
9.String concat(String str)
.
将指定字符串拼接到字符串的结尾。 等价于 +
10.int compareTo(String anotherString)
.
比较字符串的大小
11.String subString(int beginIndex)
.
返回一个新字符串,从索引
beginIndex
开始截取的字符串
12.String subString(int beginIndex,int endIndex)
.
返回一个新字符串,从索引
beginIndex
开始截取到索引endIndex-1
的字符串(左闭右开[beginIndex
,endIndex
) )
13.boolean endsWith(String suffix)
.
测试次字符串是否以指定后缀结尾
14.boolean startsWith(String prefix)
.
测试次字符串是否以指定前缀结尾
15.boolean startsWith(String prefix, int toffset)
.
测试此字符串从指定索引(
toffset
)开始的子字符串是否以指定前缀(prefix
)开始
16.boolean contains(CharSequence s)
.
当且仅当此字符串包含指定char值序列时,返回true
17.int indexOf(String str)
.
返回指定的子字符串在此字符串中第一次出现处的索引
17.int indexOf(String str, int fromIndex)
.
返回从指定索引开始,指定子字符串
str
在此字符串子串中第一次出现的索引
18.int lastIndexOf(String str)
.
同上,但是是最后一次
18.int lastIndexOf(String str, int fromIndex)
.
同上,但是是最后一次
17、18如果方法没有找到,放回都是 -1
19.String replace(char oldChar,char newChar)
.
返回一个新字符串,会用新字符
newChar
替换 旧字符oldChar
20.String replace(CharSequence target, CharSequence replacement)
.
字符序列的替换 ,字符序列用””表示
21.String replaceAll(String regex, String replacement)
.
使用指定的
replacement
替换匹配给定的正则表达式regex
的所有子字符串
22.String replaceFirst(String regex, String replacement)
.
使用指定的
replacement
替换匹配给定的正则表达式regex
的第一个子字符串
23.boolean matches(String regex)
.
告知此字符串是否匹配给定的正则表达式
24.String[] split(String regex)
.
根据给定的正则表达式的匹配拆分字符串
25.String[] split(String regex, int limit)
.
根据匹配给定的正则表达式来拆分字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中
3、字符串与包装类、基本类型之间的转换.
略,详情看 包装类
4、字符串与字符数组之间的转换.
char[] ch = {'1','2'};
//字符数组 --> 字符串
//1.String 构造器 String(char[]) 和 String(char[], int offset, int length)
String s = new String(ch);
String str = "12";
//字符串 --> 字符数组
//1. toCharArray
char c = str.toCharArray();
5、字符串与字节数组的之间的装换.
byte[] b = {1,2};
//字节数组 --> 字符串
//1. Arrays.toString
String str1 = Arrays.toString(b);
//2. 构造器
String str2 = new String(b,"utf-8");
String str = "12";
//字符串 --> 字节数组
//1. getBytes()
byte[] b = str.getBytes("utf-8");
补充.
1、String str1 = “abc”; 与 String str2 = new String(“abc”);的区别?.
- 前者 变量直接指向内存中常量池里的”abc”(”abc”作为一个对象保存在常量池中)
- 后者 变量指向新创建的String类型的对象,对象的属性value是指向内存中常量池里的”abc”
2、String str = new String(“a”);在内存中创建了几个对象.
- 两个
- 堆内存中new的String对象
- value属性对应常量池中的数据
3、不同拼接操作的对比.
- 字面量(常量)之间的连接结果,相当于还是在常量池中声明
- 有变量名参与的连接结果,会在堆上开辟空间,都相当于new
intern()
的返回值都在常量池中
String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = "helloworld";
String s5 = "hello" + s2;
String s6 = s1 + "world";
String s7 = s1 + s2;
String s8 = s5.intern();//得到的返回值 在常量池中 (不管s5的定义方式)
System.out.println(s3 == s4);//true
System.out.println(s3 == s8);//true
System.out.println(s4 == s8);//true
//其余的比较都是false
final String s9 = "hello";// s9 是常量,常量和常量之间的连接的结果是在常量池中的
4、三种JVM.
Sun公司的HotSpot
被Oracle收购
BEA公司的JRockit
- 被Oracle收购
IBM公司的J9 VM
5、Heap堆.
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保证所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分
- Young Generation Space 新生区
- Tenure Generation Space 养老区
Permanent Space 永久存储区相当于 方法区
6、Method Area方法区.
和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息、普通常量、静态变量、编译器编译后的代码等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但是它却还有一个别名叫 Non-Heap(非堆)。目的是将其和堆分开
对于HotSpot虚拟机来说,把方法区称为 永久代,但严格本质上锁两者是不同的,或者说使用永久代来实现方法区而已,永久代是方法区的一个实现,
JDK1.7
的版本中,已经将原本放在永久代的字符串常量池移走了
常量池(Constant Pool)是方法区的一部分,Class文件除了有类的版本、字段、方法、接口等描述信息,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常量
7、新生区.
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后的被垃圾回收器收集,结束生命。
新生区分为两部分
- 伊甸区(Eden Sapce):所有的类都是在这new出来的
- 幸存者区(Survivor Space):
- s0区
- s1区
当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(
Minor GC
),将伊甸园区中的不在被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区,若幸存0区满了,再对该区也进行垃圾回收,然后移到幸存1区。
如果幸存1区也满了呢?
在移动到养老区,若养老区也满了,那么这个时候将产生
Major GC
(FullGC
),进行养老区的内存清理。若养老区执行了
Full GC
之后发现依然无法进行对象的保存,就会产生OOM异常(OutOfMemoryError
)
如果出现java.lang.OutOfMemoryError
:java heap space 异常,说明java虚拟机的堆内存不够。原因:
- Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整
- 代码中创建了大量大对象,并且长时间不能被垃圾回收器收集(存在被引用)。—- 内存溢出;内存泄露
8、永久区.
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收调度,关闭JVM才会释放掉此区域所占的内存
如果出现java.lang.OutOfMemoryError
: PermGen Space 异常,说明java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包。
- 列如,在一个tomcat下部署了太多的应用,或者大量动态反射生成的类不断被加载,最终导致Perm区被占满
字符串常量池
JDK1.6及以前
常量池分配在永久代,1.6
在方法区
JDK1.7
有,但是已经逐步 “去永久代”,1.7
在堆
JDK1.8及以后
无,1.8
在元空间(MetaSpace)
9、编码和解码.
编码:String –编码字符集utf-8–> 字节 (保存数据,传输数据)
解码:字节 –解码字符集utf-8–> String (读取数据,接收数据)
编码字符集和解码字符集不一致,就导致乱码的出现