Java String基础
Java String基础
String 基础
String 实现原理?
低版本
final 的 char 数组
高版本 Java8?
final 的 byte 数组
为何用 byte 数组?
Java 中需要转义的字符
不管是 String.split(),还是正则表达式,有一些特殊字符需要转义,这些字符是:
1
( [ { / ^ - $ ¦ } ] ) ? * +
转义方法为字符前面加上 \\
,这样在 split
、replaceAll
时就不会报错了;
不过要注意,String.contains()
方法不需要转义。
字符串常量池和 intern 方法
Java 中有字符串常量池,用来存储字符串字面量! 由于 JDK 版本的不同,常量池的位置也不同:
- jdk1.6 及以下版本字符串常量池是在永久区(Permanent Generation)中
- jdk1.7、1.8 下字符串常量池已经转移到堆中了。(JDK1.8 已经去掉永久区)
String 类型的常量池比较特殊。它的主要使用方法有两种:
- 直接使用双引号声明出来的 String 对象会直接存储在常量池中
- 如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。不同版本的 intern 表现不一样
直接使用 new String() 创建出的 String 对象会直接存储在堆上
1
2
3
4
5
6
7
String str1 = "aflyun";
String str2 = new String("aflyun");
System.out.println(str1 == str2);
String str3 = str2.intern();
System.out.println(str1 ==str3);
使用 JDK1.8 版本运行输出的结果: false 和 true 。
str1 直接创建在字符串常量池中,str2 使用 new 关键字,对象创建在堆上。所以 str1 == str2 为 false。
str3 是 str2.intern(),在 jdk1.8 首先在常量池中判断字符串 aflyun 是否存在,如果存在的话,直接返回常量池中字符串的引用,也就是 str1 的引用。所以 str1==str3
为 true。
- 直接定义字符串变量的时候赋值,如果表达式右边只有字符串常量,那么就是把变量存放在常量池里。
- new 出来的字符串是存放在堆里面
- 对字符串进行拼接操作,也就是做 “+” 运算的时候,分 2 中情况
- 表达式右边是纯字符串常量,那么存放在字符串常量池里面
- 表达式右边如果存在字符串引用,也就是字符串对象的句柄,那么就存放在堆里面
一些案例
案例 1:字符串 + 号及 final 修饰
1
2
3
4
5
6
7
8
9
10
11
String str1 = "hello";
String str2 = "world";
//常量池中的对象
String str3 = "hello" + "world";
//在堆上创建的新的对象
String str4 = str1 + str2;
//常量池中的对象
String str5 = "helloworld";
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false
输出:
1
2
3
false
true
false
str1 和 str2 加了 final 修饰符后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void test1() {
// 常量池中的对象
final String str1 = "hello";
final String str2 = "world";
String str3 = "hello" + "world";
// 在堆上创建的新的对象
final String str4 = str1 + str2;
// 常量池中的对象
String str5 = "helloworld";
System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // true
System.out.println(str4 == str5); // false
}
输出:
1
2
3
true
true
true
编译器内联了
案例 2:+ new String
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void test2() {
// 同时生成堆中的对象以及常量池中hello的对象,此时str1是指向堆中的对象的
String str1 = new String("hello");
// 常量池中的已经存在hello
str1.intern();
// 常量池中的对象,此时str2是指向常量池中的对象的
String str2 = "hello";
System.out.println(str1 == str2); // false,str1.intern之前已经存在字符串池,返回的是之前new的引用
// 此时生成了四个对象 常量池中的"world" + 2个堆中的"world" +s3指向的堆中的对象(注此时常量池不会生成"worldworld")
String str3 = new String("world") + new String("world");
// 常量池没有“worldworld”,会直接将str3的地址存储在常量池内
str3.intern();
// 创建str4的时候,发现字符串常量池已经存在一个指向堆中该字面量的引用,则返回这个引用,而这个引用就是str3
String str4 = "worldworld";
System.out.println(str3 == str4); // true
}
输出:
1
2
false
true
案例 3 final 对 String 影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static void test3() {
// str1指的是字符串常量池中的 java6
String str1 = "java6";
// str2是 final 修饰的,编译时候就已经确定了它的确定值,编译期常量
final String str2 = "java";
// str3是指向常量池中 java
String str3 = "java";
// str2编译的时候已经知道是常量,"6"也是常量,所以计算str4的时候,直接相当于使用 str2 的原始值(java)来进行计算.
// 则str4 生成的也是一个常量,。str1和str4都对应 常量池中只生成唯一的一个 java6 字符串。
String str4 = str2 + "6";
// 计算 str5 的时候,str3不是final修饰,不会提前知道 str3的值是什么,只有在运行通过链接来访问,这种计算会在堆上生成 java6
String str5 = str3 + "6";
System.out.println((str1 == str4)); // true
System.out.println((str1 == str5)); // false
}
输出:
1
2
true
false
本文由作者按照 CC BY 4.0 进行授权