1. 内容简括
- 关于字符串==比较的几种情况总结
- 关于final类型变量的初始化时机的总结
2. 字符串==比较
import lombok.extern.slf4j.Slf4j;
/**
* @Author weijun.nie
* @Date 2020/4/18 21:29
* @Version 1.0
*/
@Slf4j
public class StringDemo {
public static void main(String[] args) {
String hash = "hash";
final String hashFinal = "hash";
String b = "niewj";
String a = "niewjhash";
String c = "niewj";
String d = "niewj" + "hash";
String e = "niewj" + hash;
String f = "niewj" + hashFinal;
final String hashFinal2 = getByMethod();
String g = "niewj" + hashFinal2;
log.info("字符串常量 比较: b==c -> {}", b == c); // true
log.info("字符串常量 和 两个常量的+连接: a==d -> {}", a == d); // true
log.info("字符串常量 和 一个常量连接一个变量: a==e -> {}", a == e); // false
log.info("字符串常量 和 一个常量连接一个变量: a==e.intern() -> {}", a == e.intern()); // true
log.info("字符串常量 和 一个常量连接一个final变量: a==f -> {}", a == f); // true
log.info("字符串常量 和 一个常量连接一个调用方法获取的final变量: a==g -> {}", a == g); //false
}
private static String getByMethod() {
return "hash";
}
}
1[main]- 字符串常量 比较: b==c -> true
2[main]- 字符串常量 和 两个常量的+连接: a==d -> true
3[main]- 字符串常量 和 一个常量连接一个变量: a==e -> false
4[main]- 字符串常量 和 一个常量连接一个变量: a==e.intern() -> true
5[main]- 字符串常量 和 一个常量连接一个final变量: a==f -> true
6[main]- 字符串常量 和 一个常量连接一个调用方法获取的final变量: a==f -> false
3. 分析总结:
1. 声明的字符串常量
对于字符串常量的声明, 都会推送到
字符串常量池
中去缓存: 比如String b = "niewj"; String c = "niewj";
此时, 比较两个字符串reference(也就是比较他们指向的常量字符串的内存地址), 自然都是相同的缓存;
2. 声明的两个常量字符串+操作
对于声明的两个
常量字符串
的"+"
操作,String d = "niewj" + "hash";
编译器也会做连接优化, 将其连接结果对应到字符串常量池中去比对缓存; 有则引用之; 所以此处的 a==d;
3. 声明的字符串常量 和 非final字符串引用 “+”操作
字符串常量
和非final字符串引用
的"+"
, 编译器并不会提前优化, 而是需要计算得出, 并不会在字符串常量池
中对应; 所以 a==e 为false;
4. intern()方法
字符串String类的intern()方法的说明: 我们可以看到这样的说明: String s = “abc” (1)如果池中已经有了字符串s引用的字符串(根据equals方法比较:也就是比较字符串内容), 则直接返回池中的字符串引用; (2)如果池中没有, 那么intern会生成”abc”放入池中, 并返回此串引用; (3)如果两个字符串内容相同, s1.intern()==s2.intern()肯定是true;
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.
public native String intern();
5. 常量字符串+final声明的字符串
关于
final的字符串
: final的字符串变量, 在声明时都会被放入到字符串常量池中; 不仅如此, 常量字符串和final类型字符串的"+"
操作也可以视为是 两个常量字符串的"+"
操作, 也会到池中对取缓存; 为什么呢? final类型的变量, 只能初始化1次, 而且在使用的时候, 要求必须是已经初始化过了; 所以在"+"
操作的时候, 编译器已经能确认是有值了, 可以作为常量来对待, 因此作为常量字符串对待了;
6. final声明字符串的method调用初始化
虽然也是final的字符串变量, 但是是通过
调用方法获取的final值
, 编译器无法在编译时预知
, 所以不会进行优化, 所以在"+"
操作的时候, 编译器无法确定, 因此没有同步到池;
3. final类型变量的初始化时机
另外关于final变量初始化时机的总结: final变量有3种:
(1). class中声明的实例final变量;
三种赋值时机:
1. `直接赋值`: 声明即赋值```class Const{ final int a=20; }``
2. `构造方法赋值`: 声明时不赋值, 在构造方法中初始化赋值: ```class Const{ final int a; public Const(){a = 20;}}```
3. `实例块/初始化块赋值`: 声明时不赋值,初始化块赋值:```class Const{final int a; {a = 20;}}```
(2). class中声明的static的final变量
两种赋值时机:
直接赋值
: 声明即赋值:class Const{static final int a = 20;}
静态块赋值
: 声明时不赋值,静态块赋值:class Const{static final int a; static{a = 20;}}
(3). method中声明的final的变量;
一种:使用前
初始化过即可; 特别注意的情景有一种,就是声明
了不初始化
, 也可以编译
过: 因为没有使用:
public void test(){final int a;}
可以编译过, 因为后面没有对a变量的读取;
4. 引申: 字符串常量池
:
关于字符串常量池的内存区域, 参见前面的整理: jvm字符串常量池在什么内存区域?
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com