String 類型的值能夠被反射改變從而引發的意外事件


  • 今天刷技術文章,遇到了一個問題,用 Java 反射機制去修改 String 變量的值,出於深入研究,就發現了一個問題,即,用初始值比較修改后的值,用 == or .equals() 方法,出現了相等的情況

文字描述看起來比較難受,我們直接看代碼


        //創建字符串"Hello World", 並賦給引用 s
        String s = "Hello World";
        System.out.println("s = " + s); //Hello World
        //獲取String類中的value字段
        Field valueFieldOfString = String.class.getDeclaredField("value");
        //改變value屬性的訪問權限
        valueFieldOfString.setAccessible(true);
        //獲取s對象上的value屬性的值
        byte[] value = (byte[]) valueFieldOfString.get(s);
        //改變value所引用的數組中的第5個字符
        value[5] = '_';
        System.out.println("s = " + s);  //Hello_World
        System.out.println("Hello World".toString());
        System.out.println(s == "Hello World"); // true
        System.out.println(s.equals("Hello World")); // true

如上方代碼所示,我們定義了一個 String 變量 s ,並且賦值給它 "Hello World",然后通過 Java 的反射機制去修改第五個字符的值,在輸出結果的時候,理所當然的結果是 "Hello_World",但是我在這個情況之上多了一手,把被修改后的值與初始值比較看看,會發生什么情況,結果一試就出現了問題,竟然與初始值相等且返回了 true .

於是,打算好好深究一下,我們知道 String 的變量是存放在 JVM 的常量池中的,同時指向 "Hello World" 的引用 s1 是 String 對象,存放在堆區,我們通過反射區修改的值並不是常量池中的 "Hello World" ,而是 堆區中的對象 s1 的值,所以我們輸出 s1 的時候就是 "Hello_World" ,那么為什么初始值比較修改后的值的時候會出現 true 呢?

我們看看 System.out.println("Hello World".toString()); 這行代碼,輸出結果是 Hello_World,這是因為 "Hello World".toString() 的時候,會去常量池中去找 "Hello World"的引用,它的引用就是 s1 ,所以輸出的是 s1 的值,即 "Hello_World",所以,到這里,我們就知道了,為什么 s == "Hello World" 和 s.equals("Hello World") 返回 true 了 .

[reference]

String類型的值能夠被反射改變和常量池的理解

String字符串被創建的原理過程

Java內存圖以及堆、棧、常量區、靜態區、方法區的區別

Java中 類的加載概述和加載時機


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM