java常見面試題分析之-字符串比較-延伸到final類型

文章內容包含:java

  1. 關於字符串==比較的幾種狀況總結
  2. 關於final類型變量的初始化時機的總結

字符串==比較
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

分析總結:express

1. 對於字符串常量的聲明, 都會推送到字符串常量池中去緩存: 好比 String b = "niewj"; String c = "niewj"; 此時, 比較兩個字符串reference(也就是比較他們指向的常量字符串的內存地址), 天然都是相同的緩存;
2. 對於聲明的兩個常量字符串"+" 操做 String d = "niewj" + "hash"; 編譯器也會作鏈接優化, 將其鏈接結果對應到字符串常量池中去比對緩存; 有則引用之; 因此此處的 a==d;
3. 字符串常量非final字符串引用"+", 編譯器並不會提早優化, 而是須要計算得出, 並不會在 字符串常量池 中對應; 因此 a==e 爲false;
4. 字符串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();segmentfault

5. 關於final的字符串: final的字符串變量, 在聲明時都會被放入到字符串常量池中; 不只如此, 常量字符串和final類型字符串的 "+"操做也能夠視爲是 兩個常量字符串的 "+" 操做, 也會到池中對取緩存; 爲何呢? final類型的變量, 只能初始化1次, 並且在使用的時候, 要求必須是已經初始化過了; 因此在 "+"操做的時候, 編譯器已經能確認是有值了, 能夠做爲常量來對待, 所以做爲常量字符串對待了;
6. 雖然也是final的字符串變量, 可是是經過調用方法獲取的final值, 編譯器沒法在編譯時預知, 因此不會進行優化, 因此在 "+"操做的時候, 編譯器沒法肯定, 所以沒有同步到池;
final類型變量的初始化時機

另外關於final變量初始化時機的總結:
final變量有3中:緩存

(1) class中聲明的實例final變量;

三種賦值時機:jvm

  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變量;

兩種賦值時機:優化

  1. 直接賦值: 聲明即賦值: class Const{static final int a = 20;}
  2. 靜態塊賦值: 聲明時不賦值,靜態塊賦值: class Const{static final int a; static{a = 20;}}
(3) method中聲明的final的變量;

一種:使用前初始化過便可; 特別注意的情景有一種,就是聲明不初始化, 也能夠編譯過: 由於沒有使用:this

  1. public void test(){final int a;} 能夠編譯過, 由於後面沒有對a變量的讀取;

-->引伸: 字符串常量池:關於字符串常量池的內存區域, 參見前面的整理: jvm字符串常量池在什麼內存區域?code

相關文章
相關標籤/搜索